Business Central Developer Start a project
Migration

C/AL to AL Migration: What Changes and What Breaks

July 7, 2026·8 min read·Business Central Developer

C/AL and AL share enough syntax that they look like the same language with a rename. That similarity is deceptive — the differences that matter are architectural, not cosmetic, and they're exactly the things that break silently if you convert code mechanically without understanding why the change exists.

The core shift: you can't modify base objects anymore

This is the change everything else follows from. In C/AL, customising NAV usually meant directly editing a standard object — adding a field to a page, adding a line of validation logic inside a standard codeunit. In AL, base application objects are closed. You extend them instead of editing them.

This means every C/AL customisation needs to be reclassified into one of a few AL patterns:

Converting C/AL to AL mechanically — just changing syntax — misses this entirely. The real work is correctly classifying which pattern each piece of custom logic needs.

Object ID ranges are no longer flexible

C/AL customisations often lived in whatever ID range was convenient. AL enforces object ID ranges tied to your app — typically the classic partner range (50000–99999) for on-prem, or a registered range if you're publishing to AppSource. Any custom object needs to be renumbered into a valid range, and every reference to that object elsewhere in your code needs to follow.

Syntax differences that trip people up

A non-exhaustive list of things that commonly cause conversion errors:

Reports need real rework, not just a recompile

C/AL reports using RDLC or classic layouts need their layout separated out and reattached as a proper AL report layout, and the dataset/request page structure needs to be rebuilt in the AL object syntax. This is mechanical enough to automate reliably, but it's not a find-and-replace — the object structure genuinely changes shape.

Dataports don't exist — full stop

This one catches people out because there's no direct syntax equivalent. Every Dataport object needs to become an AL XMLport, with an explicit schema hierarchy: textelement / tableelement / fieldelement. If you have file-based import/export routines built on Dataports, budget this as its own conversion task rather than assuming it falls out of a general C/AL-to-AL pass.

Events you'll rely on constantly

A few publisher event patterns come up in almost every conversion project:

[EventSubscriber(ObjectType::Table, Database::"Sales Line",
    OnAfterValidateEvent, 'Quantity', false, false)]
local procedure OnAfterValidateQuantity(var Rec: Record "Sales Line")
begin
    // runs after standard Quantity validation completes
end;

and, for the "change existing behaviour" case:

[EventSubscriber(ObjectType::Codeunit, Codeunit::"Sales-Post",
    OnBeforePostSalesDoc, '', false, false)]
local procedure OnBeforePost(var IsHandled: Boolean)
begin
    if  then
        IsHandled := true; // skip standard posting logic
end;

Knowing which of these two patterns a given piece of C/AL logic needs is most of the intellectual work in a migration. It's also exactly what a good conversion tool should classify automatically, flagging anything ambiguous for manual review rather than guessing.

Automating the mechanical part safely

We built our own conversion engine because doing this classification by hand at scale — across hundreds of objects — is slow and inconsistent between developers. A reasonable automation approach:

  1. Parse each C/AL object and diff it against the corresponding base object (if one exists)
  2. Classify each diff block: pure addition, event-subscriber candidate, or IsHandled candidate
  3. Generate the AL pattern automatically for high-confidence classifications
  4. Flag low-confidence or ambiguous blocks explicitly for a developer to review — never silently guess on something that could change financial logic

This roughly halves to a third of the manual effort of a from-scratch conversion, while keeping a human in the loop exactly where judgment is needed.

The bottom line

C/AL to AL isn't a syntax migration — it's a re-architecture of how customisations attach to the base application. Tools can automate the mechanical translation, but understanding why the extensibility model changed is what lets you convert correctly rather than just convert something that compiles.

Need help with this on your project?

We do this kind of work daily. Tell us what you're facing and we'll give you an honest read on effort and approach.

Start a project →