mirror of
https://github.com/microsoft/BCQuality.git
synced 2026-08-08 02:16:52 +01:00
Add CMFRT coding standards documentation and examples
- Introduced guidelines for "one codeunit one global function" architecture to enforce single responsibility in AL code. - Added best practices and anti-patterns for adding parameters via overloads to maintain backward compatibility. - Documented the importance of never deleting members in AL and always marking them as obsolete. - Established the requirement for OnBefore and OnAfter integration events for global procedures to enhance extensibility. - Defined naming conventions for CMFRT objects, including prefixes and object ID ranges to avoid conflicts. - Implemented patterns for case statements to ensure all cases are handled, including the necessity of an else clause. - Introduced the interface injection pattern to allow pluggable operations in table-level code. - Recommended using Confirm Management for user confirmations to improve testability. - Established a three-permission set pattern for security to ensure proper access control. - Created a review skill for CMFRT AL standards to automate compliance checks against established guidelines.
This commit is contained in:
parent
4119417ce4
commit
c72c0ad685
35 changed files with 866 additions and 0 deletions
29
custom/knowledge/naming/cmfrt-naming-prefix.bad.al
Normal file
29
custom/knowledge/naming/cmfrt-naming-prefix.bad.al
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
// Object names without the CMFRT prefix collide with other extensions.
|
||||
pageextension 2045661 "Job Card Extension" extends "Job Card"
|
||||
{
|
||||
}
|
||||
|
||||
// Unprefixed fields are indistinguishable from base application fields.
|
||||
tableextension 2045660 "Job Extension" extends Job
|
||||
{
|
||||
fields
|
||||
{
|
||||
field(2045081; "POI ID"; Guid)
|
||||
{
|
||||
DataClassification = CustomerContent;
|
||||
Caption = 'POI ID';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Unprefixed procedures have no ownership signal for reviewers.
|
||||
codeunit 2045700 "Job Management"
|
||||
{
|
||||
procedure LinkJobToPOI(JobNo: Code[20]; POIId: Guid)
|
||||
begin
|
||||
end;
|
||||
|
||||
local procedure ValidatePOIExists(POIId: Guid): Boolean
|
||||
begin
|
||||
end;
|
||||
}
|
||||
29
custom/knowledge/naming/cmfrt-naming-prefix.good.al
Normal file
29
custom/knowledge/naming/cmfrt-naming-prefix.good.al
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
// Objects use "CMFRT <ABBR> <Name>" with spaces.
|
||||
pageextension 2045661 "CMFRT GD Job" extends "Job Card"
|
||||
{
|
||||
}
|
||||
|
||||
// Fields use "CMFRT <ABBR> <FieldName>" with spaces.
|
||||
tableextension 2045660 "CMFRT GD Job Ext" extends Job
|
||||
{
|
||||
fields
|
||||
{
|
||||
field(2045081; "CMFRT GD POI ID"; Guid)
|
||||
{
|
||||
DataClassification = CustomerContent;
|
||||
Caption = 'POI ID';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Procedures use CMFRT<ABBR><ProcedureName> — concatenated, no spaces.
|
||||
codeunit 2045700 "CMFRT GD Job Mgmt"
|
||||
{
|
||||
procedure CMFRTGDLinkJobToPOI(JobNo: Code[20]; POIId: Guid)
|
||||
begin
|
||||
end;
|
||||
|
||||
local procedure CMFRTGDValidatePOIExists(POIId: Guid): Boolean
|
||||
begin
|
||||
end;
|
||||
}
|
||||
26
custom/knowledge/naming/cmfrt-naming-prefix.md
Normal file
26
custom/knowledge/naming/cmfrt-naming-prefix.md
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
---
|
||||
bc-version: [all]
|
||||
domain: naming
|
||||
keywords: [naming, prefix, cmfrt, object-name, procedure-name, field-name, abbreviation]
|
||||
technologies: [al]
|
||||
countries: [w1]
|
||||
application-area: [all]
|
||||
---
|
||||
|
||||
# CMFRT naming prefix
|
||||
|
||||
## Description
|
||||
|
||||
Every object, field, and procedure in a CMFRT extension must carry the `CMFRT XXX` prefix, where `XXX` is the product or customer abbreviation (for example `GD` for Geodynamics, `BA` for Batch Automation, `JO` for Jobs). The prefix applies to tables, table extensions, pages, page extensions, codeunits, interfaces, reports, enum values, and to all global and local procedures. Object and field names use the three-part form `CMFRT <ABBR> <Descriptive Name>` with spaces. Procedure names use the concatenated form `CMFRT<ABBR><ProcedureName>` with no spaces.
|
||||
|
||||
## Best Practice
|
||||
|
||||
Name every object `"CMFRT <ABBR> <Name>"` — for example `"CMFRT GD Job"` or `"CMFRT GD POI"`. Name every field `"CMFRT <ABBR> <FieldName>"` — for example `"CMFRT GD POI ID"`. Name every procedure `CMFRT<ABBR><ProcedureName>` — for example `CMFRTGDCalculatePOIDistance`. The procedure name must be self-describing: a reader must understand what the procedure does without reading its body. Local procedures follow the same rule.
|
||||
|
||||
See sample: `cmfrt-naming-prefix.good.al`.
|
||||
|
||||
## Anti Pattern
|
||||
|
||||
Naming objects, fields, or procedures without the CMFRT prefix — for example `procedure CalculateDiscount()` or `field(50000; "Amount"; Decimal)`. Unprefixed members collide with base application fields, break the reviewer's ability to identify extension-owned members, and violate the astena naming convention enforced across all CMFRT extensions.
|
||||
|
||||
See sample: `cmfrt-naming-prefix.bad.al`.
|
||||
25
custom/knowledge/naming/cmfrt-object-id-ranges.bad.al
Normal file
25
custom/knowledge/naming/cmfrt-object-id-ranges.bad.al
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
// ID 50000 is outside both defined CMFRT ranges and will conflict with
|
||||
// other extensions that follow the standard AppSource free range.
|
||||
table 50000 "CMFRT GD POI"
|
||||
{
|
||||
Caption = 'POI';
|
||||
DataClassification = CustomerContent;
|
||||
|
||||
fields
|
||||
{
|
||||
field(1; "Code"; Code[20]) { DataClassification = CustomerContent; }
|
||||
}
|
||||
}
|
||||
|
||||
// ID 2045081 was previously assigned to a removed object.
|
||||
// Reusing it causes silent conflicts with historical telemetry and upgrade codeunits.
|
||||
table 2045081 "CMFRT GD New Feature"
|
||||
{
|
||||
Caption = 'New Feature';
|
||||
DataClassification = CustomerContent;
|
||||
|
||||
fields
|
||||
{
|
||||
field(1; "Code"; Code[20]) { DataClassification = CustomerContent; }
|
||||
}
|
||||
}
|
||||
24
custom/knowledge/naming/cmfrt-object-id-ranges.good.al
Normal file
24
custom/knowledge/naming/cmfrt-object-id-ranges.good.al
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
// Product extension — IDs are within the product range 2045081..2046580.
|
||||
table 2045081 "CMFRT GD POI"
|
||||
{
|
||||
Caption = 'POI';
|
||||
DataClassification = CustomerContent;
|
||||
|
||||
fields
|
||||
{
|
||||
field(2045081; "Code"; Code[20]) { DataClassification = CustomerContent; }
|
||||
field(2045082; "Description"; Text[100]) { DataClassification = CustomerContent; }
|
||||
}
|
||||
}
|
||||
|
||||
// Customer extension — IDs are within the customer range 55000..55999.
|
||||
table 55000 "CMFRT JO Customer Site"
|
||||
{
|
||||
Caption = 'Customer Site';
|
||||
DataClassification = CustomerContent;
|
||||
|
||||
fields
|
||||
{
|
||||
field(55000; "Code"; Code[20]) { DataClassification = CustomerContent; }
|
||||
}
|
||||
}
|
||||
26
custom/knowledge/naming/cmfrt-object-id-ranges.md
Normal file
26
custom/knowledge/naming/cmfrt-object-id-ranges.md
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
---
|
||||
bc-version: [all]
|
||||
domain: naming
|
||||
keywords: [object-id, id-range, numbering, product-extension, customer-extension, range]
|
||||
technologies: [al]
|
||||
countries: [w1]
|
||||
application-area: [all]
|
||||
---
|
||||
|
||||
# CMFRT object ID ranges
|
||||
|
||||
## Description
|
||||
|
||||
CMFRT object IDs are partitioned into two non-overlapping numeric ranges by deployment scope. Product extensions — features delivered to all customers under the CMFRT product — use IDs from `2045081` to `2046580`. Customer-specific extensions — functionality tailored to a single customer deployment — use IDs from `55000` to `55999`. Always pick the next free ID in the correct range. A removed object's ID must never be recycled; the platform retains historical references to deleted object IDs and recycling causes silent conflicts with upgrade and telemetry systems.
|
||||
|
||||
## Best Practice
|
||||
|
||||
Before adding any AL object, identify whether the feature is product-wide or customer-specific, look up the highest currently allocated ID in the correct range across the extension's source, and assign the next sequential ID. Record ID allocations in the pull request description so reviewers can confirm the range and sequence without scanning all object files.
|
||||
|
||||
See sample: `cmfrt-object-id-ranges.good.al`.
|
||||
|
||||
## Anti Pattern
|
||||
|
||||
Assigning an ID outside both ranges, choosing a round-number ID that has no relation to the next free slot, or reusing an ID from a previously removed object. ID conflicts between extensions produce runtime application errors that are difficult to reproduce and trace, because the conflict may only manifest when both extensions are installed in the same environment.
|
||||
|
||||
See sample: `cmfrt-object-id-ranges.bad.al`.
|
||||
Loading…
Add table
Add a link
Reference in a new issue