AL extensions done right: the rules that keep upgrades painless
Business Central online is updated regularly: two major updates a year, with minor updates in between. It’s one of the benefits of the cloud, but only if your customisations keep up. A badly written extension can make an update fail, force you to postpone it or, worse, break a process the day after.
For a business owner the question is simple: will the customisations I’m paying for today hold me back tomorrow? For developers, the answer comes down to a few rules, applied every time. Here they are.
Why extensions are different from the old way
In older versions of Dynamics NAV, customisations were made by changing the standard code directly. Every upgrade became a job of comparing and merging, line by line. With AL extensions, custom code lives in a separate package and hooks into Microsoft’s application without touching it. That makes upgrades possible, but not automatically painless: it depends on how the extension is built.
Rule 1: extend, don’t replace
Events and subscribers
The main mechanism is events. Microsoft publishes hook points in the application, integration and business events, and the runtime raises trigger events on table and page operations. Your extension subscribes with an event subscriber and adds its own behaviour without modifying the original code.
A minimal example: a check on the customer name, hooked to the field’s validation event.
codeunit 50100 "Customer Checks"
{
var
NameTooShortErr: Label 'The customer name must be at least 3 characters long.';
[EventSubscriber(ObjectType::Table, Database::Customer, 'OnAfterValidateEvent', 'Name', false, false)]
local procedure CheckNameOnAfterValidate(var Rec: Record Customer; var xRec: Record Customer; CurrFieldNo: Integer)
begin
if StrLen(Rec.Name) < 3 then
Error(NameTooShortErr);
end;
}
Table and page extensions
To add fields, actions or sections, you use table extensions and page extensions. The new field lives in your extension and the standard object stays untouched.
When the event isn’t there
Sometimes the hook point you need doesn’t exist. The temptation is to copy an entire standard codeunit and change it: the quickest route today and the most expensive tomorrow, because the copy never receives Microsoft’s fixes. It’s better to look for an alternative event, rethink the flow, or ask Microsoft for a new event through its extensibility request channels.
Rule 2: don’t rely on what’s about to disappear
Microsoft doesn’t remove objects, fields or procedures overnight. First it marks them as obsolete with the ObsoleteState property, alongside ObsoleteReason, which explains what to use instead, and ObsoleteTag, which records the version in which it was deprecated. For procedures and variables, the Obsolete attribute is used.
- with the Pending state, the compiler raises a warning: the element still exists, but its days are numbered;
- with the Removed state, the element can no longer be used and code that relies on it won’t compile.
The practical rule is to treat every obsolescence warning as work to schedule straight away, not as noise. Microsoft publishes a deprecation timeline, and checking it before each major update avoids surprises. The same mechanism applies to your own extension: if you remove a field, mark it obsolete first and move the data with upgrade code.
Rule 3: handle installation and data upgrade
When a new version changes the data structure, for example a field replacing another, you need upgrade codeunits, meaning codeunits with the Subtype property set to Upgrade. They provide dedicated triggers such as OnCheckPreconditionsPerCompany, OnUpgradePerCompany and OnValidateUpgradePerCompany, with per-database variants.
For first-time installation there are install codeunits, with Subtype set to Install and triggers such as OnInstallAppPerCompany. To stop upgrade code running more than once, Microsoft recommends upgrade tags from the Upgrade Tags module of the System Application, which are more robust than simple version checks when there are many versions. The official guide on writing upgrade code for extensions shows the full pattern.
Rule 4: clear versions and dependencies
The app.json file describes the extension: version for the package version, application and platform for the minimum application and platform versions it builds on, runtime for the language version, and dependencies for the other apps it relies on.
- always increase the version with every release: an upgrade is precisely the installation of a higher version;
- don’t raise application or runtime without a reason, because you narrow the versions the extension can run on;
- keep dependencies to the essential minimum, and document them.
Rule 5: let the analysers do their job
The AL Language extension for Visual Studio Code includes four code analysers: CodeCop for the coding guidelines, PerTenantExtensionCop for extensions installed on a single tenant, AppSourceCop for apps aimed at the marketplace, and UICop for web client customisations. For a bespoke extension, the first two are the minimum.
Warnings shouldn’t be ignored out of habit. If a rule doesn’t make sense for your project, turn it off explicitly in a ruleset, with a reason, so whoever comes next knows it was a decision and not an oversight.
Rule 6: test first, in a preview environment
Before each major update, Microsoft makes preview versions available. You can create a new sandbox on the preview version, or update an existing sandbox to the preview, ideally a copy of production, with your extensions installed. That way you check both that the update succeeds and that your processes still work. Preview environments are deleted some time after general availability, so treat them as test environments. The details are on Microsoft’s page about preview environments.
Alongside manual testing, even a handful of automated tests on critical processes makes a real difference.
Rule 7: telemetry, source control and pipelines
- Telemetry: Business Central can send telemetry to Azure Application Insights, and an extension can have its own connection string in app.json. Errors, performance and upgrade outcomes become visible before users report them.
- Source control: every extension in a Git repository, with a readable history of changes. Code that exists only on one developer’s laptop is a business risk.
- Build pipelines: AL-Go for GitHub is Microsoft’s project providing templates and actions to build, test and release AL apps. Even a simple pipeline ensures every version is compiled with the analysers on and the tests run.
- Documentation: what the extension does, which events it uses, which data it adds. A few pages are enough, as long as they’re kept up to date.
What to ask whoever develops for you
Even if you’re not technical, you can ask the right questions: are all customisations extensions? Where is the source code? Are they tested on preview before updates? Who keeps track of obsolescence warnings? Clear answers are a good sign.
Let’s talk
If you’d like the extensions installed on your Business Central reviewed, or want to set up a way of developing that stands up to updates, get in touch.