X++ is the native programming language of Microsoft Dynamics 365 Finance & Operations. It's a hybrid of C# and Java with deep integration into the D365FO runtime, data access layer, and UI framework. This guide covers the patterns, practices, and anti-patterns every D365FO developer should know.
The Extension Model: The Foundation of D365FO Customization
In D365FO, all customization must be done through extensions — not by modifying (overlayering) standard Microsoft objects. This is the single most important principle in D365FO development. Extensions:
- Are upgrade-safe — Microsoft updates don't overwrite your code
- Are compatible with other ISV solutions running in the same environment
- Support continuous delivery without requiring full system updates
- Are the only customization approach supported by Microsoft
Chain of Command (CoC): The Primary Extension Pattern
Chain of Command is D365FO's method for extending class methods. It works by injecting your code into the method call chain without modifying the original class.
A CoC extension looks like this conceptually:
- Declare a class extension using
[ExtensionOf(classStr(TargetClass))] - Override the method using
finalkeyword - Call
next methodName()to invoke the original (or modified) behavior - Add your business logic before or after the
nextcall
CoC can be applied to tables, forms, data providers, report controllers, and classes — covering virtually every extension scenario.
Common X++ Extension Scenarios
Adding Custom Fields
Adding custom fields to standard D365FO tables is done through table extensions. Best practices:
- Always prefix custom fields with your ISV/company prefix (e.g.,
INV_CustomField) - Use EDT (Extended Data Types) for custom fields wherever a matching EDT exists
- Add custom fields to the appropriate field groups for display in standard forms
- Consider the data migration impact of adding fields to tables with millions of rows
Custom Workflows
D365FO's workflow engine is one of its most powerful features. Custom workflows require:
- A workflow type (defines the document the workflow applies to)
- Workflow elements (approval, task, automated task)
- A workflow document class (defines what data is available for conditions)
- Activation conditions (when the workflow triggers)
Custom Data Entities for Integration
Data entities are D365FO's primary integration interface. When building custom data entities:
- Choose the correct data entity category (Transaction, Master, Reference, Parameter)
- Use Staging tables for complex import scenarios requiring validation
- Expose entities via OData for REST-based integrations
- Implement change tracking for delta-based export scenarios
Performance Considerations in X++
Performance issues are among the most common D365FO problems we see in audits. Key performance anti-patterns:
| Anti-Pattern | Problem | Correct Approach |
|---|---|---|
| Nested while select loops | N+1 query problem, exponential database calls | Use joins in while select, or aggregate queries |
| Select without index hints | Full table scans on large tables | Always use appropriate index hints |
| RecordInsertList not used | Individual inserts for large datasets | Use RecordInsertList for bulk inserts |
| Set-based operations avoided | Row-by-row processing in batch | Use update_recordset and delete_from |
| Locking contention | Long transactions holding locks | Use optimistic concurrency, minimize transaction scope |
Testing X++ Customizations
D365FO provides a built-in unit testing framework (SysTest) that should be used for all custom development:
- Write test classes that extend
SysTestCase - Use
assertEquals,assertTrue, and other assertion methods - Mock external dependencies using
SysTestMockAttribute - Run tests in the Azure DevOps pipeline as part of CI/CD
Upgrade Safety: Keeping Customizations Current
Microsoft releases D365FO updates every month. Every update must be tested against your customizations:
- Subscribe to Microsoft's release plans and review breaking changes
- Run automated regression tests against every update before promoting to production
- Use LCS for automated update management and testing
- Avoid tight coupling between custom code and internal Microsoft APIs that may change