Skill v1.0.1
currentAutomated scan100/100+3 new
version: "1.0.1" name: skill-migrate description: "AL version migration for Business Central. Use when upgrading extensions between BC versions, handling breaking changes, or implementing rollback strategies."
Skill: AL Project Migration
Purpose
Plan and execute BC platform version upgrades: app.json configuration, breaking-change remediation, deprecated API replacement, event signature updates, upgrade codeunits for data migration, rollback strategy, and manifest regeneration.
When to Load
This skill should be loaded when:
- Upgrading an AL extension to a newer BC platform version (e.g., BC 23.x → 24.x)
- Fixing compilation errors after updating the runtime or platform property
- Replacing deprecated AL patterns (C/AL legacy, obsolete APIs)
- Updating event subscriber signatures after base-app changes
- Writing upgrade codeunits for schema changes or data migration
- Generating a full deployment package after migration
Core Patterns
Pattern 1: App.json Platform Update
Update the three version-sensitive properties in app.json:
{"platform": "25.0.0.0","runtime": "14.0","application": "25.0.0.0","dependencies": [{"id": "63ca2fa4-4f03-4f2b-a480-172fef340d3f","name": "System Application","publisher": "Microsoft","version": "25.0.0.0"}],"features": ["TranslationFile", "GenerateCaptions", "NoImplicitWith"]}
Rules:
platform— target BC platform version (major.minor.0.0)runtime— AL runtime version matching the target (see runtime matrix)application— must match or be compatible with target platform- Update all
dependenciesversions to match the target release - Add new
featuresflags required by the target runtime (e.g.,NoImplicitWithfrom runtime 11.0+)
Pattern 2: Deprecated Code Replacement
Replace legacy patterns with modern equivalents:
// ❌ Deprecated — C/AL styleRecord.FIND('-');Record.FINDSET(TRUE, TRUE);IF Record.FINDFIRST THEN;Record.INIT;Record.INSERT;// ✅ Modern — AL patternsRecord.FindSet();Record.FindSet(true, true);if Record.FindFirst() then;Record.Init();Record.Insert(true);
// ❌ Deprecated — WITH statement (removed in NoImplicitWith)with SalesHeader do begin"Document Type" := "Document Type"::Order;"Sell-to Customer No." := CustomerNo;Insert(true);end;// ✅ Modern — explicit record referenceSalesHeader."Document Type" := SalesHeader."Document Type"::Order;SalesHeader."Sell-to Customer No." := CustomerNo;SalesHeader.Insert(true);
// ❌ Deprecated — TextConst (runtime < 6.0)CustomerNotFoundErr@1000 : TextConst 'ENU=Customer %1 not found.';// ✅ Modern — LabelvarCustomerNotFoundErr: Label 'Customer %1 not found.';
Pattern 3: Event Signature Migration
When base-app events add or change parameters between versions:
// BC 23.x subscriber — 3 parameters[EventSubscriber(ObjectType::Codeunit, Codeunit::"Sales-Post",OnBeforePostSalesDoc, '', false, false)]local procedure OnBeforePost(var SalesHeader: Record "Sales Header";CommitIsSuppressed: Boolean;var IsHandled: Boolean)begin// ...end;// BC 24.x — same event now has 4 parameters (new PreviewMode added)[EventSubscriber(ObjectType::Codeunit, Codeunit::"Sales-Post",OnBeforePostSalesDoc, '', false, false)]local procedure OnBeforePost(var SalesHeader: Record "Sales Header";CommitIsSuppressed: Boolean;PreviewMode: Boolean;var IsHandled: Boolean)begin// ...end;
Migration steps:
- Build with
al_build— signature mismatches produceAL0482errors - Use
al_get_object_definitionto inspect the new publisher signature - Update parameter list to match exactly (name, type, order)
- Re-verify with
al_build
Pattern 4: Obsolete Object Handling
Handle objects marked ObsoleteState = Removed in the target version:
// Step 1: Find usage of removed objects// al_search_objects — search for the obsolete table/page/codeunit// Step 2: Replace with the designated successor// Before (removed in BC 24):// Codeunit 80 "Sales-Post (Yes/No)" — ObsoleteState = Removed// After:Codeunit.Run(Codeunit::"Sales-Post", SalesHeader);// Step 3: Check ObsoleteReason for migration guidance// ObsoleteReason typically says: "Use codeunit X instead"
Process:
- Compile → collect all
AL0503(removed) andAL0432(pending) warnings - For
Removed— must fix before compilation succeeds - For
Pending— fix proactively to avoid future breaks - Check release notes for each removed object's replacement
Pattern 5: Upgrade Codeunit (Data Migration)
Use upgrade codeunits to transform data when schema changes between versions:
codeunit 50100 "Contoso Data Upgrade"{Subtype = Upgrade;trigger OnUpgradePerCompany()varModule: Info;beginModule.DataVersion(1); // check data version trackingMigrateCustomerLoyaltyData();SplitAddressFields();end;trigger OnValidateUpgradePerCompany()begin// Pre-upgrade validation — runs before OnUpgradePerCompanyVerifyDataIntegrity();end;local procedure MigrateCustomerLoyaltyData()varCustomer: Record Customer;ContosoLoyalty: Record "Contoso Loyalty Entry";UpgradeTag: Codeunit "Upgrade Tag";beginif UpgradeTag.HasUpgradeTag(GetLoyaltyMigrationTag()) thenexit;// Migrate data from old field to new tableCustomer.SetFilter("Contoso Legacy Points", '>0');if Customer.FindSet() thenrepeatContosoLoyalty.Init();ContosoLoyalty."Customer No." := Customer."No.";ContosoLoyalty.Points := Customer."Contoso Legacy Points";ContosoLoyalty."Entry Date" := WorkDate();ContosoLoyalty.Insert();until Customer.Next() = 0;UpgradeTag.SetUpgradeTag(GetLoyaltyMigrationTag());end;local procedure GetLoyaltyMigrationTag(): Code[250]beginexit('CONTOSO-LOYALTY-MIGRATION-20260301');end;}
Upgrade codeunit rules:
Subtype = Upgrade— BC runs these automatically during app upgrade- Use
UpgradeTagto ensure idempotency — never run migration twice OnValidateUpgradePerCompanyruns first — validate data before transformingOnUpgradePerCompanyruns per company — do the actual migration- Always test with a copy of production data before deploying
- For large datasets, use
SelectLatestVersion()and batch processing
Pattern 6: Rollback Strategy
Document and prepare rollback before executing migration:
## Rollback Plan — {Project} Migration v{X} → v{Y}### Pre-Migration Checklist-[ ] Git branch created from stable tag: `git checkout -b migration/vX-to-vY vX.0.0`-[ ] Database backup taken and verified-[ ] Extension .app file of current version archived-[ ] Rollback tested in sandbox environment### Rollback Procedure1.**Code rollback**: `git checkout vX.0.0` — restore previous version2.**Extension rollback**: Uninstall new version, install archived .app3.**Data rollback** (if upgrade codeunit ran):-Restore database from pre-migration backup-OR run compensating downgrade codeunit (if written)4.**Verify**: Run smoke tests on restored environment### Point of No Return⚠️ After these actions, rollback requires database restore:-Upgrade codeunits that DELETE data-Schema changes that DROP columns-External system notifications sent
Rollback rules:
- Always create a rollback plan BEFORE starting migration
- Tag the pre-migration commit:
git tag vX.0.0-pre-migration - Archive the current .app file alongside the plan
- Test rollback in sandbox — never assume it works
- If upgrade codeunit is destructive (deletes data), document the point of no return
Workflow
Step 1: Pre-Migration Assessment
- Backup: Ensure source control is up to date (
git statusclean) - Download current symbols:
al_downloadsymbols - Document dependencies:
al_packages— list loaded packages with current versions - Review release notes: Check BC target version breaking changes
- Create migration plan in
.github/plans/{project}-migration.md
PAUSE — wait for user approval before modifying files.
Step 2: Update Configuration
- Update
app.json(Pattern 1) — platform, runtime, application, dependencies, features - Download new symbols for target version
- Build:
al_build— collect all errors
Step 3: Fix Compilation Errors
Prioritize by error type:
- AL0503 (Removed objects) → Pattern 4
- AL0482 (Event signature mismatch) → Pattern 3
- AL0432 (Deprecated usage) → Pattern 2
- Other errors → case-by-case analysis
For each fix, verify with incremental build.
Step 4: Regenerate and Validate
- Update the manifest (
app.json) — no agent tool; edit directly (or via the VS Code command) - Full build:
al_build— zero errors, zero new warnings;al_buildalso produces the.apppackage - Run existing tests to verify no regressions
Step 5: Post-Migration
- Update CHANGELOG with migration notes
- Tag the commit with new version
- Test in sandbox environment before production
Version-Specific Notes
| Upgrade Path | Key Breaking Changes | |
|---|---|---|
| BC 20 → 21 | New permission model, page layout changes | |
| BC 21 → 22 | Namespace support, NoImplicitWith enforcement | |
| BC 22 → 23 | Async patterns, isolated events, security hardening | |
| BC 23 → 24 | New AL capabilities, event parameter additions | |
| BC 24 → 25 | Enhanced debugging, agent integration, new runtime features |
References
- Choosing the Runtime Version
- Breaking Changes per Release
- ObsoleteState Property
- App.json Properties
- NoImplicitWith Feature
- Upgrade Codeunits
- Upgrade Tags
Constraints
- This skill covers version migration planning, breaking-change remediation, and configuration updates
- Do NOT modify base BC objects — extension-only changes
- Do NOT skip the pre-migration backup and assessment step
- Do NOT combine migration with feature development — migrate first, then develop
- Always create a migration plan and obtain approval before modifying files
- Event debugging →
skill-debug.md| Performance after migration →skill-performance.md| Test verification →skill-testing.md