There is a new sheriff in town (Business Central)
There is a new sheriff in town

BusinessCentral.LinterCop is deprecated and ALCops is the new sheriff in town.
Well, actually, there are six:
| Analyzer | Description |
|---|---|
| ApplicationCop | Enforces Business Central application conventions for tables, pages, enums, labels, and permissions. |
| DocumentationCop | Validates that AL code is properly documented with comments and XML documentation. |
| FormattingCop | Enforces consistent code formatting and visual structure. |
| LinterCop | Flags code quality issues, measures complexity, and promotes modern AL patterns. |
| PlatformCop | Detects code that is technically broken, dangerous, or silently ignored at the AL platform level. |
| TestAutomationCop | Validates the structure and correctness of AL test code. |
Motivation
If you need to know more about the motivation for this change, you can find the explanation in Stefan Maron’s article Introducing ALCops and watch this video on YouTube: ALCops: LinterCop’s Successor for Business Central AL Code Analysis
Migration from BusinessCentral.LinterCop to ALCops
I have made an enhanced version of the LinterCop Migration Script, which you can find on GitHub here: https://github.com/finnpedersenkazes/LinterCop-Migration-Script.
Original: https://alcops.dev/docs/lintercop-migration-script/
Installation
Find the VS Code extension on the Visual Studio Code Marketplace.
Or install it directly from Visual Studio Code.
Source: Getting Started
Don’t forget to uninstall the BusinessCentral.LinterCop extension.
Setup
Look for ALCops in the menu bar at the bottom of VS Code or open the Command Palette (Ctrl+Shift+P) and search for ALCops. Then select ALCops: Select Code Analyzers.
settings.json
{
"al.enableCodeAnalysis": true,
"al.codeAnalyzers": [
"${CodeCop}",
"${UICop}",
"${PerTenantExtensionCop}",
"${analyzerFolder}ALCops.ApplicationCop.dll",
"${analyzerFolder}ALCops.DocumentationCop.dll",
"${analyzerFolder}ALCops.FormattingCop.dll",
"${analyzerFolder}ALCops.LinterCop.dll",
"${analyzerFolder}ALCops.PlatformCop.dll",
"${analyzerFolder}ALCops.TestAutomationCop.dll",
"${analyzerFolder}ALCops.Common.dll"
],
"alcops.automaticUpdates": true,
"alcops.updateNotification": "notify-only",
"alcops.versionChannel": "stable",
"alcops.checkUpdateInterval": 24,
"al.backgroundCodeAnalysis": "Project",
"al.browser": "Edge",
"al.ruleSetPath": "../.BlueCircle360Certified/Level-4/Main.ruleset.json",
}
Don’t forget to remove the old BusinessCentral.LinterCop settings.
What is new?
My code is normally warning-free.
So what did ALCops detect in my recent project?
| Rule ID | Description | Category | Code Fix | Severity |
|---|---|---|---|---|
| PC0037 | Use Validate() instead of direct field assignment | Design | Yes | Warning |
| PC0030 | Use partial records on read operation | Performance | Yes | Info |
| PC0029 | Use CreateSequentialGuid for key fields | Performance | Yes | Info |
| DC0007 | Public objects must include XML documentation comments | Design | No | Info |
| DC0009 | Events must include XML documentation comments | Design | No | Hidden |
| AC0032 | Unused permission declared | Design | Yes | Info |
I have now started reviewing these cases to better understand what should be done.
Conclusion
AL code analysis helps you keep your code cleaner, safer, and faster.
It helps you find problems early, which will save you time and money.
But more importantly, it creates a coding standard for your team, for now and in the future.
Clean Code is about respect for the developer coming after you.
Reach out to me if you need help getting started with AL code analysis.
Observations
FC0004 Permission declarations should be ordered alphabetically
I ended up disabling FC0004 in my permissionset.al files because it conflicted with auto save.
LC0095 Parameter is not referenced
LC0095 had to be disabled in EventSubscriber Codeunits because most functions don’t use all parameters.
PC0037 Use Validate() instead of direct field assignment
PC0037 forced me to move all TableRelation properties from Table Fields to Page Fields, which probably is a good thing.
The TableRelation property provides two things. Control that the value is already in the related table and a lookup link. I needed the lookup link, so I moved the property to the field on the page. I am not testing the page.
I am writing super fast tests using Temporary tables. Using Validate() everywhere collided with checking Table Relations on the table. Since I aime for 100% code coverage of the objects I test, I moved the Table Relation property to the page.
I prioritized to keep my tests simple and fast, at the expense of having to keep track of the Table Relation on both the Card Page and the List Page. I hope I made the right decision.
Recommendations
How to disable a rule in a special case
Sometimes you have to accept an antipattern or wait for the base application to catch up.
LC0088: Prefer Enum over Option type
In this case, the base function only accepts an option. For readability, I have given it one.
#pragma warning disable LC0088
[NonDebuggable]
internal procedure CreateHash(TextJson: Text; SecretKey: SecretText) Hash: SecretText
var
CryptographyManagement: Codeunit System.Security.Encryption."Cryptography Management";
HashAlgorithmType: Option HMACMD5,HMACSHA1,HMACSHA256,HMACSHA384,HMACSHA512;
begin
Hash := CryptographyManagement.GenerateHash(TextJson, SecretKey, HashAlgorithmType::HMACSHA256);
end;
#pragma warning restore LC0088
Event Subscriber Codeunit
Always keep your Event Subscribers in a dedicated Codeunit.
LC0095: Parameter is not referenced
Typically, you are not using all the parameters the Event Publisher is offering, therefore you need to disable the rule.
Start and end your object file with a #pragma warning to disable the rule for the entire Codeunit.
#pragma warning disable LC0095
codeunit 50000 "Event Subscribers"
{
...
}
#pragma warning restore LC0095
Clean AL Code Initiative
- Part 1: Rulesets in Business Central
- Part 2: Namespaces in AL
- Part 3: VS Code Extensions for AL
- Part 4: Automated Tests in AL
- Part 5: Advanced CodeCop Analyzer and Custom Rulesets
- Part 6: How to make a code review
- Part 7: Preconditions and TryFunctions in AL
- Part 8: There is a new sheriff in town