A typical Business Central tenant runs base BC, three or four AppSource apps, and a couple of per-tenant extensions someone’s partner built. Every one of those was tested. Microsoft tested base BC. Each ISV tested their app against base BC. The partner tested the PTE against base BC plus, if you were lucky, the apps that were installed at the time.
Nobody tested the combination you are actually running.
Every vendor validated their own code against a system none of your users will ever use, base Business Central with nothing else installed.
This is not a criticism of any vendor. Testing every possible combination of AppSource apps is not something an ISV can do, and it is not something Microsoft can do either. The combination is unique to you, which means responsibility for validating it lands with you by default rather than by decision.
This piece covers why the extension model created this specific problem, four ways extensions break each other, why Business Central’s update cadence multiplies the exposure, and what to actually validate.
What the Extension Model Solved, and What It Created
Worth a short recap, because the trade-off explains the failure mode.
In the NAV era, customization meant modifying base objects directly in C/AL. It worked, and upgrades were brutal, every modified object had to be merged against the new version by hand, which is why so many NAV customers ran versions five years behind.
The extension model fixed that. Extensions live alongside the base application rather than inside it, hooking in through event subscribers and adding fields through table and page extensions. Base objects stay untouched, so Microsoft can update the base app without merging anything, and customers can stay current.
That was a genuine improvement and nobody wants to go back. What it introduced is that customization is now distributed across independently versioned packages from independent vendors, each of which hooks into shared base behavior without knowing what else is hooking into the same places.
C/AL made upgrades painful and interactions visible. Extensions made upgrades easy and interactions invisible.
Four Ways Extensions Break Each Other
1. Event subscriber ordering
When two extensions subscribe to the same event, both listening to OnAfterPostSalesDoc, say, both run. The order in which they run is not something either vendor controls or can rely on.
Most of the time that is fine, because the subscribers do unrelated things. The failure case is when both modify the same field or record. One writes a value, the other overwrites it, and which one wins depends on ordering neither developer specified. Both extensions work perfectly in isolation. Together, one silently loses.
This is the hardest class to diagnose because the symptom appears in data rather than in an error, and reproducing it requires the same combination in the same order.
2. Field and enum collisions on shared tables
Two extensions adding fields to the same table is normal and safe, the ID ranges prevent literal collisions. The problem is semantic. Two apps that each add their own notion of a customer classification, both populated, both read by different logic, produce a record where two fields disagree about the same thing.
Nothing errors. Reporting built on one field and validation built on the other quietly diverge.
3. Dependency chains that shift underneath you
Extensions can depend on other extensions, declared in app.json. When app A depends on app B, updating B can change behavior A relies on, and A’s vendor tested against the version of B that existed at the time.
The dependency graph in a mature tenant is rarely something anyone has drawn. It becomes visible during an upgrade, when an installation fails and someone works out the ordering constraints for the first time.
4. Upgrade codeunits doing overlapping data work
Extensions migrate their own data during upgrade through upgrade code units. Two extensions transforming the same records during the same upgrade is a state most testing never covers, because upgrade paths are tested per-extension against a clean base.
This one matters more than its frequency suggests, because upgrade failures are hard to unwind. A functional bug can be fixed forward; a bad data migration may need a restore.
The Monthly Cadence Multiplier
Business Central updates monthly, with two major release waves a year on top. Each installed extension updates on its own vendor’s schedule, which is not coordinated with Microsoft’s or with each other’s.
The consequence is that the specific combination running in your tenant changes several times a year, in permutations nobody planned. Base BC moves in March. App A updates in April. App B updates in April as well, having been tested against the March base but not against April’s version of App A.
| Component | Update Cadence | Tested Against |
| Base Business Central | Monthly + 2 major waves | Microsoft’s own test suite; not your extensions |
| AppSource apps | Each vendor’s own schedule | Base BC, plus whatever the ISV chose to test |
| Per-tenant extensions | Your partner’s schedule | Base BC plus the apps present at build time |
| Your actual combination | Changes several times a year | Nobody |
This is materially faster than the Finance and Operations cadence, and it is why extension validation in Business Central cannot reasonably be a manual pre-upgrade exercise. At monthly frequency, anything requiring a coordinated human regression cycle gets skipped, and skipping it is a defensible decision right up until the month it is not.
Obsolete Tags: The Warning Nobody Reads
Microsoft does not remove base functionality without notice. Objects, fields and procedures being retired are marked with an obsolete state, a reason and a target version, usually with a runway of a year or more before removal.
The system works. What fails is that the warning surfaces in compiler output during extension builds, and the people who see that output are the extension’s developers rather than the customer running it. If the ISV is actively maintaining the app, the warning gets addressed. If the app is stable and lightly maintained, which describes a lot of niche AppSource apps and most per-tenant extensions built three years ago, nobody is compiling it, so nobody sees the warning.
The warning existed for eighteen months. It appeared in a build log for an app nobody has rebuilt since 2023.
Then the removal lands, and an extension that has worked reliably for years stops working in a monthly update, with no change on the customer side and frequently no vendor still actively supporting it.
Worth an occasional audit: which of your installed extensions have had a version update in the last year, and which have not. The ones that have not are the exposure.
Why Sandbox Testing Under-Represents the Risk
Sandbox environments are the right place to validate an update, and two things routinely make them less representative than assumed.
The first is extension parity. Sandboxes are frequently created from a production copy at a point in time and then diverge, an app added in production after the sandbox was refreshed, a PTE deployed to production directly during an incident. Testing an update against a sandbox with a different extension set validates a system nobody runs.
The second is data volume and shape the test data management. Extension logic frequently branches on data conditions, a customer classification present, a dimension populated, a historical document type that only exists in older records. A sandbox with recent data or a subset does not exercise the branches that legacy records reach.
What to Validate
The organizing principle is that you cannot validate extensions individually and infer that the combination works. The combination is the thing that has to be tested.
| Validate | How | Catches |
| Business outcomes with the full extension set | End-to-end process validation in an environment matching production’s installed apps | Event ordering conflicts and semantic field collisions |
| Event-heavy processes specifically | Posting routines, approval workflows, document release, where subscribers cluster | Extensions overwriting each other’s field writes |
| After every base update, not every wave | Automated regression on the monthly cadence | Removals that hit unmaintained extensions |
| After any extension update, not just base | Same regression, triggered by app version change | Cross-extension dependency drift |
| Upgrade paths with the real extension set | Run the upgrade in a sandbox with production’s exact app list | Overlapping upgrade codeunit data work |
| Sandbox extension parity | Compare installed app lists between environments before testing | Validating a combination nobody runs |
The last row takes five minutes and invalidates a surprising amount of testing when it comes back mismatched. Worth doing before anything else on this list.
The third and fourth rows together are the cadence point: the trigger for regression should be any version change in the stack, not only Microsoft’s major waves. Extension updates are the more frequent event and the less watched one.
Where Sofy makes the difference:
Sofy Dynamics 365 Test Agent validates Business Central at the business-outcome level, at the data layer, which is the approach the combination problem actually requires.
The relevant property is that an outcome assertion does not need to know which extension produced a result. If a sales order posts with the wrong dimension because two extensions wrote to the same field in an unlucky order, the assertion fails, without anyone having mapped the subscriber graph or knowing the conflict existed. You are testing what the system produces rather than what each component intends.
That also makes monthly-cadence regression practical. Validation that adapts to base and extension changes rather than breaking on them can run on every update, which is the frequency the platform actually requires, rather than the frequency a manual regression cycle can sustain.
For the Business central payables agent testing processes where subscribers cluster most densely, the same data-layer validation applies as for first-party functionality, the extension layer does not need separate treatment because the assertion is on the resulting document either way.
What This Doesn’t Replace
Three things that remain necessary and are good at their jobs.
- AL test codeunits and the BC test toolkit. Microsoft ships a substantial test library, and extension developers writing AL tests against their own code is the right practice. That is unit and integration testing of a component. It is not a substitute for validating the combination, and the combination is not a substitute for it.
- AL-Go and build pipelines. Automated build, test and deployment for extensions is where obsolete warnings surface and where breaking changes get caught early. If your PTEs are not in a pipeline, that is worth fixing independently of anything here.
- Vendor testing. ISVs testing against base BC is the correct division of labor, they cannot test your combination and should not be expected to. The gap is not that vendor’s under-test; it is that the combination has no owner by default.
Frequently Asked Questions
Why do Business Central extensions conflict if they don’t modify base objects?
Because they hook into shared behavior rather than modifying it. Two extensions can subscribe to the same event, both listening to a posting routine, for instance, and both will run, in an order neither vendor controls. If both modify the same field, one silently overwrites the other. Both extensions work correctly in isolation; the conflict only exists in combination.
Who is responsible for testing the extension combination?
By default, nobody, which is the core problem rather than a criticism of any party. Microsoft tests base BC, each ISV tests their app against base BC, and your partner tests per-tenant extensions against whatever was installed at build time. The specific combination running in your tenant is unique to you, so validating it lands with you by default rather than by decision.
What are obsolete tags and why do they cause problems?
Microsoft marks retiring objects, fields and procedures with an obsolete state and a target version, usually with a year or more of runway. The mechanism works; the gap is that the warning appears in compiler output during extension builds. If an app is stable and lightly maintained, nobody is rebuilding it, so nobody sees the warning, and the extension stops working when the removal lands.
How often should Business Central extension testing run?
On any version change in the stack, not only on Microsoft’s major release waves. Base BC updates monthly, and each extension updates on its own vendor’s schedule, so the combination changes several times a year. Extension updates are both the more frequent trigger and the less watched one.
Does testing extensions replace AL test codeunits?
No, and the two answer different questions. AL tests validate a component’s own logic and belong with the extension developer. Outcome-level validation covers what the combination produces in your tenant, which no component-level test can see. Both are needed; neither substitutes for the other.
The Bottom Line
The extension model was the right architectural decision. It made Business Central upgradeable, ended the merge misery of the C/AL era, and let customers stay current in a way that was genuinely not possible before.
The cost is that customization is now distributed across independently versioned packages from independent vendors, hooking into shared behavior without visibility of each other. Every party tests their own piece correctly, and the assembled system, the only one that actually runs, has no owner.
The practical response is to test the assembly rather than the parts: outcome-level validation across the full extension set, triggered by any version change rather than only major waves, in an environment whose installed apps genuinely match production.
Test the Combination You Actually Run
See outcome-level validation across your full extension set, catching conflicts nobody mapped, on the monthly cadence Business Central actually updates at.
