What if you looked at a piece of AL code, and the more you looked, the more bugs you found? Help me create such a masterpiece. Check out the video for details:

In this video, Erik kicks off a fun community challenge: writing the worst piece of AL code ever — code that looks perfectly fine on the surface but is riddled with subtle bugs that would cause it to fail at runtime or behave completely unexpectedly. This isn’t about shaming anyone’s code; it’s about exploring the sneaky gotchas in AL that can trip up even experienced developers.
The Inspiration: Hidden Bugs in Plain Sight
The idea for this challenge came from two places. First, Erik discovered that his AL compiler toolbox didn’t support calling Format on a record variable — something he’d simply never implemented. While exploring how Format works with records, he stumbled onto some undocumented behavior. Second, a discussion on Twitter (attributed to Thomas) about writing AL code for job interviews that looks correct at first glance but is full of bugs sparked the creative challenge.
Discovering Undocumented Format Behavior
Erik demonstrates that you can call Format on a record variable, which returns tab-separated field values. The function signature allows you to specify a length and a format number:
Message('%1', Format(Rec, 40, 0));
Format numbers 0, 1, 2, and 9 (XML format) work for record types. But if you try format number 3, you get an error: “Standard format number 3 does not exist for the type Record.” There’s no Microsoft documentation covering which format numbers are valid for records — you have to discover it by trial and error.
This is exactly the kind of subtle trap that makes for great “worst code” material. At first glance, Format(Rec, 40, 3) looks perfectly reasonable — many other data types support format number 3 — but it will blow up at runtime.
Anatomy of the Worst Code
Let’s look at the complete code Erik assembles, piece by deceptive piece:
pageextension 50100 CustomerListExt extends "Customer List"
{
trigger OnOpenPage();
var
CustomerToFind: Text;
begin
CustomerToFind := '10000...30000';
Rec.setrange(Name, CustomerToFind);
repeat
until Rec.Next(-1) = 1;
message('%1', Format(Rec, 40, 0));
end;
}
This compiles without errors and produces no AL warnings. But it’s absolutely loaded with problems. Let’s break them down:
Bug #1: SetRange Instead of SetFilter
The code uses Rec.SetRange(Name, CustomerToFind) where CustomerToFind contains '10000...30000'. The three dots (...) look like a filter range expression, and a developer might assume this filters for customers between 10000 and 30000.
But SetRange treats the value as a literal — it’s looking for a customer whose Name field is exactly the string '10000...30000'. To use a filter expression with wildcards and ranges, you’d need SetFilter instead. This is a classic mix-up that compiles perfectly but produces completely wrong results.
Bug #2: Filtering on the Wrong Field
Even if we switched to SetFilter, we’re filtering on the Name field with what looks like customer numbers. The values 10000 through 30000 are typically customer No. values, not customer names. So the filter is applied to the wrong field entirely.
Bug #3: An Infinite Loop
The repeat...until loop is the most devious part:
repeat
until Rec.Next(-1) = 1;
Rec.Next(-1) steps backward through the record set. The loop continues until Next(-1) returns 1, meaning it successfully moved back one record. But here’s the trap: we keep stepping backward, and we’ll only stop when we successfully step backward — which means once we hit the first record and can’t go back any further, Next(-1) returns 0, and the loop continues. Then there’s nowhere to go. This creates an infinite loop that will never terminate.
The Challenge: A “Where’s Waldo” for AL Code
Erik compares this exercise to a “Where’s Waldo” puzzle — everything looks normal and fine at a glance, but when you look closely, you start spotting problems everywhere. The code compiles cleanly. There are no warnings. But it will never work correctly.
Erik invites the community to contribute their own examples of deceptively broken AL code — snippets that look innocent but hide nasty bugs. He plans to assemble the best submissions into a Part 2 video, creating the ultimate “worst piece of AL ever written.”
Summary
This video is a great exercise in sharpening your AL code review skills. The key takeaways are:
- SetRange vs. SetFilter —
SetRangetreats values as literals, not filter expressions. If you need range or wildcard filtering, useSetFilter. - Know your fields — Filtering on the wrong field (Name vs. No.) is an easy mistake that compiles without complaint.
- Watch your loop logic —
Next(-1)in arepeat...untilcan create infinite loops if the exit condition can never be met. - Format on records is undocumented — Only format numbers 0, 1, 2, and 9 work for record types. Using others will cause a runtime error despite compiling fine.
Stay tuned for Part 2, where Erik will compile the community’s best (worst?) contributions into the ultimate collection of subtly broken AL code.