One of the hardest things to get right in AL is when to use business logic hidden away in codeunits and when it’s OK to just write simple code. In this video I explore one of these cases, check it out:

In this video, Erik tackles a common but dangerous anti-pattern in Business Central AL development: directly modifying fields that are meant to be controlled by dedicated codeunits. This practice bypasses important business logic and events, potentially breaking other extensions that rely on those events being triggered properly.
The Problem: Shortcutting Business Logic
The scenario is straightforward and inspired by real-world situations Erik has encountered far too often. A customer asks for a way to reopen a sales order. A developer looks at the Status field on the Sales Header, sees it’s just an enum with no validation code directly on it, and writes something like this:
pageextension 50100 MyPage extends "Sales Order"
{
actions
{
addfirst(processing)
{
action(ReleaseAction)
{
Caption = 'My Release Function';
trigger OnAction()
var
SalesReleaseManagement: Codeunit "Release Sales Document";
begin
//SalesReleaseManagement.Reopen(Rec);
Rec.Validate(Status, Rec.Status::Open);
Rec.Modify(true);
end;
}
}
}
}
On the surface, this looks correct — even diligent. The developer used Validate instead of a direct assignment, and they called Modify(true) to ensure any OnModify triggers fire. It compiles, it runs, and the status changes to Open. Job done, right?
Wrong.
Why This Is Dangerous
The Status field on the Sales Header is not meant to be edited directly. Its value is controlled by a dedicated codeunit: Codeunit 414 — “Release Sales Document”. This codeunit contains functions like Reopen and Release (along with manual release variants), and critically, these functions do much more than just flip a status field:
- Business logic checks: The codeunit verifies sales lines are in a valid state before allowing a release or reopen operation.
- Events: The codeunit raises integration events (like
OnBeforeReopen,OnAfterReopen, etc.) that other extensions subscribe to. - Data integrity: Other apps depend on these events to maintain correct data and trigger their own workflows.
When you bypass the codeunit and directly validate the field, none of those events fire. Any extension that subscribes to those events — to enforce rules, synchronize data, or trigger downstream processes — will simply never know the status changed.
The Correct Approach
Instead of manipulating the field directly, use the codeunit that owns the business logic:
pageextension 50100 MyPage extends "Sales Order"
{
actions
{
addfirst(processing)
{
action(ReleaseAction)
{
Caption = 'My Release Function';
trigger OnAction()
var
SalesReleaseManagement: Codeunit "Release Sales Document";
begin
SalesReleaseManagement.Reopen(Rec);
end;
}
}
}
}
By calling SalesReleaseManagement.Reopen(Rec), you ensure that all business logic executes, all validation checks run, and all events are raised. Every other extension that depends on those events will work correctly.
The Hidden Challenge: There’s No Signpost
Erik highlights what makes this problem particularly insidious: there is no indication on the field itself that it should only be modified through a specific codeunit.
- There’s no property on the field saying “this field is controlled by Codeunit 414.”
- There’s no compiler warning if you validate it directly from outside that codeunit.
- There’s no documentation or tooltip guiding you to the correct approach.
- The field might show as non-editable on the page, but code can still write to it freely.
The only way to know the correct pattern is to open the base application, find the relevant codeunits, and read through the code — which, as Erik notes, is a substantial amount of code with no obvious entry point for discovery.
A Suggestion for Microsoft
Erik suggests that Microsoft could help by introducing some kind of property or metadata on fields that indicates which objects are authorized to control them. If a developer tries to modify such a field outside of those designated objects, the compiler could surface a warning or a code analysis hint. This wouldn’t be a breaking change but would guide developers toward the correct patterns.
The Ripple Effect: Breaking Apps Tomorrow
Perhaps the most important point Erik makes is about timing. Your shortcut might not break anything today. Everything works, tests pass, the customer is happy. But tomorrow, the customer installs another extension — one that subscribes to the OnAfterReopen event to perform critical data synchronization. Because your code never triggers that event, the new extension silently fails, data becomes inconsistent, and the root cause is incredibly difficult to trace.
This is the real danger: you’re not just potentially breaking your own code — you’re potentially breaking every other extension in the ecosystem that follows the correct patterns.
Summary
The takeaway is simple but vital: before directly modifying a field in Business Central, always check whether there’s a codeunit that manages that field’s value. Fields like Status on sales documents, posting fields, approval fields, and many others have dedicated codeunits with business logic and events that must be respected. Use those codeunits. Don’t shortcut the business logic — even when the shortcut appears to work perfectly. The cost of getting it wrong might not be visible until it’s far too late.