All Around Microsoft Dynamics Business Central
Confirm Can Keep Your Transaction OpenConfirm looks harmless.
It is just a yes/no dialog, right?
But in Business Central, there is a nasty habit that shows up again and again in custom code: developers do some work, touch one or more records, and then call Confirm() before the code finishes.
That is a bad pattern because the user is now part of your transaction.
And while the user is thinking, clicking, getting coffee, or being interrupted by a Teams call, the transaction can stay open.
In AL, write transactions are started automatically when needed and are normally committed when the code finishes. If you display a Confirm dialog after you have already modified data or taken locks, you are effectively pausing that unit of work in the middle.
From a technical point of view, the code is waiting for user input, but the transaction has not been completed yet.
That means other sessions may have to wait for locks to be released.
The result is usually not dramatic in development. In production, though, it often turns into exactly the kind of issue that is hard to reproduce and annoying to diagnose:
That is why Confirm inside a write transaction is such a bad habit. It hides a concurrency problem behind a perfectly innocent UI dialog.
Here is the kind of code that should make you nervous:
local procedure UpdateCustomerCreditLimit(CustomerNo: Code[20]; NewLimit: Decimal)
var
Customer: Record Customer;
begin
Customer.Get(CustomerNo);
Customer.LockTable();
Customer."Credit Limit (LCY)" := NewLimit;
Customer.Modify();
if not Confirm('Do you want to continue with the related updates?') then
exit;
DoMoreWork(Customer);
end;
The problem is not the question itself.
The problem is when the question is asked.
At that point, the code has already changed data. If the user waits 30 seconds before answering, that transaction may stay open for 30 seconds as well. If the user goes for lunch, you now have a much bigger problem.
Yes, it works.
That is exactly why this pattern survives.
It works in happy-path tests, and it works when there is only one developer in a sandbox. The damage shows up later, under concurrency, when several users or background processes hit the same data.
This is one of those patterns that is functionally correct and operationally dangerous.
If you need user confirmation, get it before you start changing data.
local procedure UpdateCustomerCreditLimit(CustomerNo: Code[20]; NewLimit: Decimal)
var
Customer: Record Customer;
begin
if not Confirm('Do you want to update the credit limit and continue with related updates?') then
exit;
Customer.Get(CustomerNo);
Customer.LockTable();
Customer."Credit Limit (LCY)" := NewLimit;
Customer.Modify();
DoMoreWork(Customer);
end;
This small reorder changes a lot:
If the question is purely about whether the operation should start, ask it at the beginning.
Commit() before Confirm?Some developers solve this by inserting a Commit() before the dialog:
Customer.Modify();
Commit();
if not Confirm('Continue?') then
exit;
Yes, this can release locks before the user interaction.
But it also splits your work into multiple transactions, and that is a business decision, not just a technical cleanup.
Once you commit, the first part is permanent. If the user answers No, or if the next step fails, you cannot pretend the whole operation was still one atomic unit.
So Commit() is not the universal fix. Sometimes it is the right tool, but only when you consciously accept the new transaction boundary.
When you see Confirm, ask yourself one question:
"Has this code already modified data or taken locks before the user is asked?"
If the answer is yes, stop and rethink the flow.
That simple review habit catches a surprising number of hidden blocking issues.
I most often see this problem in:
In all of these cases, the dialog often feels user-friendly, but it can quietly turn into a lock-duration problem.
Confirm is not bad.
Using Confirm inside an open write transaction is bad.
So my default recommendation is:
Commit() as an explicit transaction design choice, not a quick escape hatchModify(), Insert(), Delete(), or explicit lockingThat one habit can remove a surprising amount of avoidable blocking from a Business Central solution.
Whenever a user dialog appears in the middle of data-changing code, remember what is really happening:
your database work may now be waiting for a human.
And humans are much slower than SQL Server.