Use CreateSequentialGuid for key fields (PC0029)
When assigning a new GUID to a field that is part of a table key, use Guid.CreateSequentialGuid() instead of CreateGuid(). Random GUIDs cause SQL index fragmentation because their values are scattered across the index B-tree. Sequential GUIDs are generated in increasing order, which keeps index inserts append-only and reduces page splits by 20–40%.
This rule tracks the flow of CreateGuid() values through variable assignments and procedure calls (within the same module) to determine whether the generated GUID ultimately lands in a key field.
A code fix is available for this diagnostic.
Example
The following code assigns a random GUID to a primary key field:
codeunit 50100 MyCodeunit
{
procedure InsertRecord()
var
MyTable: Record MyTable;
begin
MyTable."Entry Id" := CreateGuid(); // Use CreateSequentialGuid for key fields [PC0029]
MyTable.Insert();
end;
}
table 50100 MyTable
{
fields
{
field(1; "Entry Id"; Guid) { }
field(2; Description; Text[100]) { }
}
keys
{
key(PK; "Entry Id") { }
}
}
To fix this, use Guid.CreateSequentialGuid():
codeunit 50100 MyCodeunit
{
procedure InsertRecord()
var
MyTable: Record MyTable;
begin
MyTable."Entry Id" := Guid.CreateSequentialGuid();
MyTable.Insert();
end;
}
table 50100 MyTable
{
fields
{
field(1; "Entry Id"; Guid) { }
field(2; Description; Text[100]) { }
}
keys
{
key(PK; "Entry Id") { }
}
}
Scenarios that are not flagged
- Assigning
CreateGuid()to a Guid field that is not part of any key - Assigning
CreateGuid()to fields in temporary tables (no SQL backing) - Passing
CreateGuid()as an argument to event publishers (idiomatic AL pattern) - Passing
CreateGuid()to procedures in external dependencies where the source code is not available
Configuration
By default, this rule only flags CreateGuid() calls whose value flows to a field that is part of a table key (primary or secondary). This conservative scope targets the cases with the clearest performance benefit: key fields back SQL indexes, and random GUIDs fragment those indexes.
However, sequential GUIDs can improve performance beyond just key fields. As Duilio Tacconi points out
, replacing all CreateGuid() calls with Guid.CreateSequentialGuid() is a straightforward performance win in most scenarios.
The reason this rule doesn’t flag everything by default is a security consideration. As Stefano Demiliani notes , sequential GUIDs are predictable. If you expose GUIDs publicly (in URLs, APIs, or external integrations), a caller could guess adjacent values. Random GUIDs provide better obfuscation of data patterns in those scenarios.
To flag all CreateGuid() calls regardless of whether the value ends up in a key field, add the following to your alcops.json:
{
"UseSequentialGuidScope": "AllGuidFields"
}
This is useful for teams that want to audit every CreateGuid() usage and make a conscious decision per call site.