Can you cheat the Change Log in Business Central?

Someone asked me the other day: Can you cheat the change log in Business Central? Join me in investigating whether you can circumvent the Change Log:

https://youtu.be/rv6-thLJbQ0


Someone recently asked Erik whether it’s possible to bypass the Change Log in Business Central — and honestly, he wasn’t sure. The documentation isn’t entirely clear about which actions get recorded and which don’t. We all know that changes made through the UI are captured, but what about modifications made from AL code, RecordRefs, bulk operations, or configuration packages? In this video, Erik puts the Change Log through a series of tests to find out if it can be cheated.

Setting Up the Test Environment

Erik starts with a fresh Business Central Docker image running version 26.2. He has configured the Change Log Setup to track the Customer table (table 18), specifically monitoring the Number, Name, and Credit Limit fields.

First, he creates a new customer record through the UI — “Mr. Change Log” — with a credit limit of 3,400. Navigating to the Change Log Entries page (and filtering on table number 18, since the page requires filtering on a field that isn’t even visible by default), he confirms three entries: an insert on the Number field, and modifications for Name and Credit Limit. Everything recorded as expected.

He then changes the credit limit from 3,400 to 2,400 in the UI, and that modification is also properly captured with old and new values. So far, no surprises — the UI is fully tracked.

Test 1: Modifying Records from AL Code with Validate and Modify(true)

Now the real tests begin. Erik writes AL code in a page extension on the Customer List to modify the credit limit programmatically:

Customer.Get('C00010');
Customer.Validate("Credit Limit (LCY)", 4000);
Customer.Modify(true);

After running this, the Change Log shows the old value of 2,400 and the new value of 4,000. Result: Recorded.

Test 2: Modify(false) — Skipping Triggers

What if you call Modify(false) instead, explicitly skipping triggers?

Customer.Get('C00010');
Customer.Validate("Credit Limit (LCY)", 5000);
Customer.Modify(false);

Still recorded in the Change Log. Result: Recorded.

Test 3: Direct Field Assignment (No Validate)

Erik takes it a step further — what if you skip the Validate call entirely and assign the field value directly?

Customer.Get('C00010');
Customer."Credit Limit (LCY)" := 6000;
Customer.Modify(false);

The Change Log still catches it. Result: Recorded.

Test 4: Using RecordRef and FieldRef

RecordRef and FieldRef are lower-level constructs that work with records and fields indirectly through references rather than strongly-typed record variables. Erik tests whether this approach can slip past the Change Log:

Customer.Get('C00010');
Ref.GetTable(Customer);
FR := Ref.Field(Customer.FieldNo("Credit Limit (LCY)"));
FR.Value := 7000;
Ref.Modify();

The Change Log entries show the update from 6,000 to 7,000. Result: Recorded.

Test 5: ModifyAll — Bulk Operations

The ModifyAll function updates all records matching the current filters in a single operation. Surely this bulk approach might bypass the log?

Customer.ModifyAll("Credit Limit (LCY)", 8000, false);

Checking the Change Log, all customer records now show the update to 8,000 — every single one was individually logged. Result: Recorded.

Test 6: Configuration Packages

For the final test, Erik tries something completely different: using a Configuration Package. He creates a new package for the Customer table, exports it to Excel, changes the credit limit to 9,000 in the spreadsheet, imports it back, and applies the package.

Six records are modified — and every one of them appears in the Change Log with the updated value. Result: Recorded.

The Complete Test Code

Here’s the full page extension Erik used to run his code-based tests. The commented-out sections show each approach he tried sequentially:

namespace DefaultPublisher.CheatTheChangeLog;

using Microsoft.Sales.Customer;

pageextension 50100 CustomerListExt extends "Customer List"
{
    trigger OnOpenPage();
    var
        Customer: Record Customer;
        Ref: RecordRef;
        FR: FieldRef;
    begin
        // Test 1-3: Direct record manipulation
        // Customer.Get('C00010');
        // Customer.Validate("Credit Limit (LCY)", 4000);  // Test 1 & 2
        // Customer."Credit Limit (LCY)" := 6000;           // Test 3
        // Customer.Modify(true);                            // or Modify(false)

        // Test 4: RecordRef and FieldRef
        // Ref.GetTable(Customer);
        // FR := Ref.Field(Customer.FieldNo("Credit Limit (LCY)"));
        // FR.Value := 7000;
        // Ref.Modify();

        // Test 5: Bulk operation
        // Customer.ModifyAll("Credit Limit (LCY)", 8000, false);
    end;
}

Summary of Results

  • UI changes — ✅ Recorded
  • Modify(true) with Validate — ✅ Recorded
  • Modify(false) with Validate — ✅ Recorded
  • Direct field assignment + Modify(false) — ✅ Recorded
  • RecordRef/FieldRef + Modify — ✅ Recorded
  • ModifyAll (bulk operation) — ✅ Recorded
  • Configuration Package import — ✅ Recorded

Conclusion

The answer is clear: No, you cannot cheat the Change Log in Business Central. Whether you modify data through the UI, from AL code using standard record operations, through lower-level RecordRef/FieldRef manipulations, via bulk operations like ModifyAll, or even through Configuration Package imports — every change is faithfully recorded in the Change Log.

This is good news from an auditing and compliance perspective. The Change Log operates at a level deep enough in the platform that the typical AL-level techniques for modifying data cannot circumvent it. If you think of another approach that Erik didn’t test, let him know in the comments!