Dynamics 365 Development Best Practice Guide
This guide provides the best practices you need to optimize the following Dynamics 365 (D365) functions:
- Add a publisher prefix
- Manage customizations in custom solutions (not default solutions)
- Add detailed component descriptions
- Export unmanaged and managed solutions for every release
- Create separate, unmanaged solutions for ribbon customizations
Adding a prefix to solution components that’s relative to you or your organization enables users to quickly identify who created or customized each component.
Customizations created within default solutions cannot be exported or distributed to other environments. In addition, the default solutions use their default publisher, which results in an unspecified/uncontrolled publisher being assigned to components. To avoid these issues, create custom components (e.g., entities, web resources, modern apps, processes, security roles) within custom solutions. If you want to customize out-of-the-box (OOB) components, add them to the custom solution before customization.
Adding detailed descriptions to entities, forms, views, fields, and processes prevents confusion and enables fellow developers to easily identify what the component is for.
Unmanaged solutions can be used to reconfigure the system or set up a new instance if the development environment fails. Managed solutions must be used to deploy customizations in higher environments (QA, pre-production, and production).
Ribbon workbench imports entire solutions, recreates each entity ribbon, and then publishes them. Fewer entities reduce ribbon update time.
- Use unique field names
- Choose the correct field data type
- Create alternate keys for fields or group of fields
- Check ‘Disable most recently used items for this field’ setting for filtered lookup fields
Duplicate field names may cause an error while importing data, or confuse users creating personal views, Power Automate flows, and more.
Choose a data type that accurately reflects the data you want to store. If your data contains null values, ensure the data type you select supports null values.
For example: If time is not required for a date, then choose the “Date Only” field data type. Or, if a field allows only two values without any default value, then choose the “Option Set” data type.
This prevents the user from creating records with duplicate field values or combination of field values.
Warning: Once the key is deployed to the managed environment, it cannot be deleted.
This prevents users from selecting unrelated options from recent searches.
For example: If the lookup fields are ‘State’ and ‘City’, disable most recently used items for ‘City’. If not disabled, users may be able to select a city from recent searches irrelevant to the selected state.
- Enable field mapping
Enable field mapping to auto-populate field data from a parent record when a child record is created (common for sub-grids).
- Remove asynchronous function calls when saving quick create forms
- Register Event Handlers for field events programmatically
On quick create forms, the Save & Close button does not wait for asynchronous function calls to complete (e.g. retrieve record WebAPI). If possible, remove asynchronous calls when saving quick create forms and instead execute the calls upon form load, or upon change of field values.
- Use only alphanumeric characters for view names
- Limit view columns to show only required fields
- For sub-grid views, which show related records, don't add a parent column to view
- Add icons for categorical fields
- Ensure columns and relative orders are the same in active and inactive views
Using special characters for view names may cause issues when importing from Excel.
Optional fields may not have data and may increase the size of the grid to accommodate empty cells.
If you add a parent column to sub-grid views, the same value will be shown for all related records.
For example: If a state entity is added as a sub-grid in a country entity, don't add a country column in sub-grid view of state, as it will be the same for every country record.
Adding icons for status, gender, rank, profit, loss, etc. enables users to quickly identify fields.
This ensures consistency in information available for records across both states.
- Replace the app’s GUID with a user-friendly URL name
Use the app’s URL suffix to access the app instead of the app’s globally unique identifier (GUID). Using the URL suffix is more user-friendly.
- Update the stage of Business Process Flow (BPF) with custom fields
Instead of performing moveNext() and movePrevious() operations from scripts, update custom fields (e.g. status, gender) from scripts. This triggers the out-of-the-box (OOB) background workflow (registered on BPF Entity) to update the active stage in BPF.
- Use RELATIVE paths to reference web resources
- Bind preSearch lookup field event only on form load and use a global variable for dynamic custom filtration and update it whenever required
- Use Xrm.Constants
- Open forms with navigation popup
- Use browser-specific web developer tools to debug your code
This ensures the web resource references are environment independent, as absolute path references include the environment name.
This saves time by enabling developers to quickly reference and reuse common code.
ExecutionContext is a parent object of FormContext, which enables not only form event handlers, but grid event handlers also using GridContext.
This ensures high performance and easy maintenance of code related to custom filtration of lookup fields.
Use Xrm.Constants to access constants like attribute required levels (e.g., none, required, recommended), attribute types (e.g., Boolean, date/time, lookup), and form notification levels (e.g. error, warning).
Use Xrm.Navigation.navigateTo to open forms (entity record, entity list or HTML web resource) as a modal dialog instead of using Xrm.Internal.openDialog or jQuery custom dialog frameworks which are either deprecated or unsupported.
- Use context.depth when updating fields using plug-ins
- Update entity by creating a new instance with required fields only
- Declare main execution function as static
- Create separate C# files to declare constants and XML expressions and to handle custom and system exceptions
- Limit plug-in files to a maximum of 8MB
If context.depth is not used, it causes infinite recursive plug-in calls.
Instead of directly updating a case entity like service.Update(case), create a new entity using Entity temp = new Entity('case'), temp['x'] = 'value', service.Update(temp)). This creates a new instance with required fields only, which avoids unnecessary field updates and effort.
All other helper functions should be declared as private to reduce code duplication and improve performance.
Creating separate files improves code readability.
Microsoft Dynamics CRM plug-ins have a maximum plug-in size. Exceeding that limit prevents remaining plug-ins from being imported.
- Create a solution in Power Apps - Power Apps – Microsoft Docs, last updated June 14, 2023
- Define alternate keys to reference records – Microsoft Docs, last updated March 30, 2023
- Write and register a plug-in (Microsoft Dataverse) - Power Apps – Microsoft Docs, last updated February 21, 2023
- navigateTo (Client API reference) in model-driven apps - Power Apps – Microsoft Docs, last updated November 29, 2022
- Build your first model-driven app – Microsoft Docs, last updated February 22, 2022
- Change the prefix for the default publisher – Microsoft Docs, last updated February 15, 2022
- Default solution vs. custom solution - Power Platform – Microsoft Docs, last updated February 15, 2022
- Create or edit mapping between entity fields – Microsoft Docs, last updated February 15, 2022
- Configure event handlers for a form in Dynamics 365 – Microsoft Docs, last updated February 15, 2022
- Create web resources in Dynamics 365 – Microsoft Docs, last updated February 15, 2022
Dynamics 365 Deployment Best Practices
Learn More →