3.4 Extension Maintenance, Deprecation & Breaking Changes

Key Takeaways

  • In the cloud extension model, published database tables, fields, and public APIs cannot be directly deleted or altered destructively without causing breaking changes for dependent extensions and tenant databases.
  • The AL deprecation lifecycle enforces a phased transition from ObsoleteState = Pending (compiler warnings for external callers) to ObsoleteState = Removed (compiler errors for external callers while preserving physical SQL schema).
  • Every obsoleted element must specify ObsoleteReason (describing why the element was deprecated and naming its replacement) and ObsoleteTag (tracking the version of deprecation for lifecycle auditing).
  • Upgrade codeunits (Subtype = Upgrade) execute set-based data transformations using the high-performance DataTransfer object directly at the SQL database layer, eliminating slow record-by-record AL loops.
  • The AppSourceCop code analyzer verifies extension source code against baseline packages (.app) to detect breaking changes, enforce Semantic Versioning rules, and validate deprecation compliance before release.
Last updated: August 2026

3.4 Extension Maintenance, Deprecation & Breaking Changes

Maintaining extensions in a cloud-first, continuous-update ecosystem requires disciplined lifecycle management. Because hundreds of customer tenants and dependent third-party extensions may rely on an application's database schema, events, and public procedures, developers cannot delete or fundamentally alter existing objects without triggering breaking changes. Microsoft enforces a structured Deprecation Lifecycle supported by AL compiler properties, upgrade codeunits, and the AppSourceCop code analyzer.


1. Breaking Changes and Extension Immutability Rules

A breaking change is any modification to an extension that causes dependent extensions to fail compilation or causes data loss in existing customer databases.

What Constitutes a Breaking Change in AL?

  1. Schema Breaking Changes:
    • Deleting an existing table, table field, or table extension.
    • Decreasing the length of a Text or Code field (e.g., Code[50] to Code[20]).
    • Changing the data type of an existing field (e.g., Integer to Decimal or Text to Enum).
    • Modifying the fields that comprise a table's Primary Key.
    • Deleting or changing the numeric ID of an Enum value.
  2. Code & API Breaking Changes:
    • Deleting or renaming a public codeunit, page, query, report, or interface.
    • Deleting a public procedure or changing its signature (adding parameters, changing parameter types, or changing return values).
    • Deleting or changing the signature of an Integration or Business Event publisher.
    • Reducing the access modifier of an object or method from public to internal or local.

2. The AL Deprecation Workflow: ObsoleteState, ObsoleteReason & ObsoleteTag

To retire outdated fields, objects, or procedures safely, developers must transition elements through the formal Obsolete Lifecycle rather than deleting them immediately.

Active / Normal  ──>  ObsoleteState = Pending  ──>  ObsoleteState = Removed  ──>  Schema Removal (Major Version Upgrade)
(Fully supported)     (Compiler Warning)            (Compiler Error to Callers)    (Physical cleanup after data migrated)

The Three ObsoleteState Options

ObsoleteState ValueCompilation ImpactRuntime / Execution ImpactPrimary Use
No (Default)Normal compilation; no diagnostics.Normal execution.Active production code.
PendingEmits a Compiler Warning (AL0432 / AL0433) whenever referenced.Fully operational at runtime.Notice of upcoming deprecation; gives developers time to migrate.
RemovedEmits a Compiler Error (AL0432) if external or dependent code references it.Exists in database schema; accessible only within the declaring extension's upgrade codeunits.Final stage before physical schema deletion; prevents new dependencies while data is migrated.

Deprecation Properties in Table Field Definitions

When deprecating a table field, developers must specify ObsoleteState, ObsoleteReason, and ObsoleteTag:

table 50100 "Contoso Customer Tier"
{
    Caption = 'Contoso Customer Tier';
    DataClassification = CustomerContent;

    fields
    {
        field(1; "Customer No."; Code[20])
        {
            Caption = 'Customer No.';
            TableRelation = Customer;
        }
        
        // Deprecated legacy field
        field(2; "Discount Percent"; Decimal)
        {
            Caption = 'Discount Percent';
            DataClassification = CustomerContent;
            ObsoleteState = Pending;
            ObsoleteReason = 'Replaced by Tier Code (field 3) and the new Tier Discount Matrix.';
            ObsoleteTag = '24.0'; // Target version when marked obsolete
        }
        
        // New replacement field
        field(3; "Tier Code"; Code[10])
        {
            Caption = 'Tier Code';
            TableRelation = "Contoso Loyalty Tier";
            DataClassification = CustomerContent;
        }
    }
}

Deprecating Procedures and Code Elements with [Obsolete]

For procedures, codeunits, interfaces, and control add-ins, use the [Obsolete] attribute:

codeunit 50105 "Contoso Discount Engine"
{
    [Obsolete('Use CalculateTierDiscount(CustomerNo, Amount) instead.', '24.0')]
    procedure CalculateDiscount(CustomerNo: Code[20]): Decimal
    begin
        // Legacy fallback implementation
        exit(10.0);
    end;

    procedure CalculateTierDiscount(CustomerNo: Code[20]; Amount: Decimal): Decimal
    begin
        // Modern implementation
        exit(15.0);
    end;
}
Loading diagram...
Data Migration and Deprecation Architecture

3. High-Performance Data Migration with the DataTransfer Object

When transitioning data from obsolete fields to new schema structures during an extension upgrade, developers write an Upgrade Codeunit (Subtype = Upgrade).

In modern AL, rather than using slow record-by-record loops (FindSet -> repeat ... Modify() until Next() = 0), developers use the DataTransfer object. DataTransfer operates directly at the SQL Server database engine level (generating high-performance INSERT INTO ... SELECT or set-based UPDATE statements), completing migrations in milliseconds even across millions of rows.

DataTransfer Migration Pattern in an Upgrade Codeunit

codeunit 50110 "Contoso Tier Upgrade"
{
    Subtype = Upgrade;

    trigger OnUpgradePerCompany()
    var
        DataTransfer: DataTransfer;
        CustomerTier: Record "Contoso Customer Tier";
        UpgradeTag: Codeunit "Upgrade Tag";
        ContosoUpgradeTag: Label 'Contoso-TierMigration-2026', Locked = true;
    begin
        // Ensure upgrade logic runs exactly once per company
        if UpgradeTag.HasUpgradeTag(ContosoUpgradeTag) then
            exit;

        // Configure set-based SQL bulk copy from old field to new field
        DataTransfer.SetTables(Database::"Contoso Customer Tier", Database::"Contoso Customer Tier");
        DataTransfer.AddFieldValue(
            CustomerTier.FieldNo("Discount Percent"), 
            CustomerTier.FieldNo("Tier Code")
        );
        DataTransfer.CopyFields();

        // Set upgrade tag to prevent re-execution
        UpgradeTag.SetUpgradeTag(ContosoUpgradeTag);
    end;
}

Upgrade Codeunit Execution Triggers

  • OnCheckPreconditionsPerCompany / OnCheckPreconditionsPerDatabase: Validates prerequisites (e.g., verifying external service connectivity or database consistency) before upgrade execution commences.
  • OnUpgradePerCompany / OnUpgradePerDatabase: Executes the core data migration logic for each company or across the global database tenant.
  • OnValidateUpgradePerCompany / OnValidateUpgradePerDatabase: Validates that data was successfully transformed and migrated before committing the transaction.

4. AppSourceCop Breaking Change Detection & Semantic Versioning

The AppSourceCop code analyzer is a specialized static analysis tool provided by Microsoft to validate that an extension does not introduce breaking changes across versions and adheres to AppSource marketplace requirements.

Configuring AppSourceCop.json

To enable breaking change detection in VS Code, create an AppSourceCop.json configuration file in the project root referencing the prior baseline version package:

{
  "name": "Contoso Quality Management",
  "publisher": "Contoso Ltd.",
  "version": "1.0.0.0",
  "baselinePackageCachePath": "./.alpackages/baseline"
}

When configured, AppSourceCop compares the current source code against the baseline .app file stored in the cache path and raises diagnostic errors if breaking modifications are detected:

  • AS0001: Tables and table extensions that have been published must not be deleted.
  • AS0002: Fields must not be deleted.
  • AS0004: Fields must not change type (key-field changes are caught separately by AS0009).
  • AS0018: A procedure belonging to the public API cannot be removed.

Semantic Versioning (SemVer) Guidelines for Extensions

Extension versions follow the format Major.Minor.Build.Revision:

  • Major Increment (2.0.0.0): Removal of elements previously marked with ObsoleteState = Removed, major structural redesigns, or breaking changes.
  • Minor Increment (1.1.0.0): Introduction of new functionality, new fields, and marking existing elements with ObsoleteState = Pending.
  • Build / Revision Increment (1.0.1.0): Non-breaking bug fixes, performance optimizations, and documentation/translation updates.
Test Your Knowledge

When obsoleting a table field in AL using ObsoleteState = Pending, which two additional properties are required to ensure compliance with Business Central lifecycle governance and code analyzers?

A
B
C
D
Test Your Knowledge

A developer has marked an existing table field with 'ObsoleteState = Removed' in a published extension. What is the effect of this setting when another dependent extension attempts to compile against this field?

A
B
C
D
Test Your Knowledge

When migrating millions of records from an obsolete table field to a newly introduced replacement field in an Upgrade Codeunit, which AL mechanism provides the highest performance by executing set-based operations directly in the database tier?

A
B
C
D
Test Your Knowledge

How does the AppSourceCop code analyzer verify whether a developer has introduced breaking changes in a new release of a Business Central extension?

A
B
C
D