There is a new sheriff in town

Robot Sheriff and Dinosaur in jail

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

Super fast tests covering 100% of your code