---
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

For BC29 / AL18, first read [AL18 capability checks](references/al18-capabilities.md)
when selecting new manifest, public-resource, isolation, testing or translation
features. Apply only the sections needed by the requirement; distinguish announced
capabilities from locally verified declarations. Keep BC28 configuration unchanged
unless an upgrade is part of the approved task.

### Pattern 1: App.json Platform Update

Update the three version-sensitive properties in `app.json`:

```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](https://learn.microsoft.com/en-us/dynamics365/business-central/dev-itpro/developer/devenv-choosing-runtime))
- `application` — must match or be compatible with target platform
- Resolve each dependency by identity, minimum version, target runtime and required APIs; do not require every library version to numerically match the application version.
- Add new `features` flags required by the target runtime (e.g., `NoImplicitWith` from runtime 11.0+)

### Pattern 2: Deprecated Code Replacement

Replace legacy patterns with modern equivalents:

```al
// ❌ Deprecated — C/AL style
Record.FIND('-');
Record.FINDSET(TRUE, TRUE);
IF Record.FINDFIRST THEN;
Record.INIT;
Record.INSERT;

// ✅ Modern — AL patterns
Record.FindSet();
Record.FindSet(true, true);
if Record.FindFirst() then;
Record.Init();
Record.Insert(true);
```

```al
// ❌ 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 reference
SalesHeader."Document Type" := SalesHeader."Document Type"::Order;
SalesHeader."Sell-to Customer No." := CustomerNo;
SalesHeader.Insert(true);
```

```al
// ❌ Deprecated — TextConst (runtime < 6.0)
CustomerNotFoundErr@1000 : TextConst 'ENU=Customer %1 not found.';

// ✅ Modern — Label
var
    CustomerNotFoundErr: Label 'Customer %1 not found.';
```

### Pattern 3: Event Signature Migration

When base-app events add or change parameters between versions:

```al
// 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:**
1. Build with `al_build` — signature mismatches produce `AL0482` errors
2. Use `al_get_object_definition` to inspect the new publisher signature
3. Update parameter list to match exactly (name, type, order)
4. Re-verify with `al_build`

### Pattern 4: Obsolete Object Handling

Handle objects marked `ObsoleteState = Removed` in the target version:

```al
// 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:**
1. Compile → collect all `AL0503` (removed) and `AL0432` (pending) warnings
2. For `Removed` — must fix before compilation succeeds
3. For `Pending` — fix proactively to avoid future breaks
4. 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:

```al
codeunit 50100 "Contoso Data Upgrade"
{
    Subtype = Upgrade;

    trigger OnUpgradePerCompany()
    var
        Module: Info;
    begin
        Module.DataVersion(1);      // check data version tracking
        MigrateCustomerLoyaltyData();
        SplitAddressFields();
    end;

    trigger OnValidateUpgradePerCompany()
    begin
        // Pre-upgrade validation — runs before OnUpgradePerCompany
        VerifyDataIntegrity();
    end;

    local procedure MigrateCustomerLoyaltyData()
    var
        Customer: Record Customer;
        ContosoLoyalty: Record "Contoso Loyalty Entry";
        UpgradeTag: Codeunit "Upgrade Tag";
    begin
        if UpgradeTag.HasUpgradeTag(GetLoyaltyMigrationTag()) then
            exit;

        // Migrate data from old field to new table
        Customer.SetFilter("Contoso Legacy Points", '>0');
        if Customer.FindSet() then
            repeat
                ContosoLoyalty.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]
    begin
        exit('CONTOSO-LOYALTY-MIGRATION-20260301');
    end;
}
```

**Upgrade codeunit rules:**

- `Subtype = Upgrade` — BC runs these automatically during app upgrade
- Use `UpgradeTag` to ensure idempotency — never run migration twice
- `OnValidateUpgradePerCompany` runs first — validate data before transforming
- `OnUpgradePerCompany` runs 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:

```markdown
## 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 Procedure
1. **Code rollback**: `git checkout vX.0.0` — restore previous version
2. **Extension rollback**: Uninstall new version, install archived .app
3. **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

1. **Backup**: Ensure source control is up to date (`git status` clean)
2. **Download current symbols**: `al_downloadsymbols`
3. **Document dependencies**: `al_packages` — list loaded packages with current versions
4. **Review release notes**: Check BC target version breaking changes
5. **Create migration plan** in `.github/plans/{project}-migration.md`

**PAUSE — wait for user approval before modifying files.**

### Step 2: Update Configuration

1. Update `app.json` (Pattern 1) — platform, runtime, application, dependencies, features
2. Download new symbols for target version
3. Build: `al_build` — collect all errors

### Step 3: Fix Compilation Errors

Prioritize by error type:
1. **AL0503** (Removed objects) → Pattern 4
2. **AL0482** (Event signature mismatch) → Pattern 3
3. **AL0432** (Deprecated usage) → Pattern 2
4. **Other errors** → case-by-case analysis

For each fix, verify with incremental build.

### Step 4: Regenerate and Validate

1. Update the manifest (`app.json`) — no agent tool; edit directly (or via the VS Code command)
2. Full build: `al_build` — zero errors, zero new warnings; `al_build` also produces the `.app` package
4. Run existing tests to verify no regressions

### Step 5: Post-Migration

1. Update CHANGELOG with migration notes
2. Tag the commit with new version
3. 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](https://learn.microsoft.com/en-us/dynamics365/business-central/dev-itpro/developer/devenv-choosing-runtime)
- [Breaking Changes per Release](https://learn.microsoft.com/en-us/dynamics365/business-central/dev-itpro/upgrade/deprecated-features-w1)
- [ObsoleteState Property](https://learn.microsoft.com/en-us/dynamics365/business-central/dev-itpro/developer/properties/devenv-obsoletestate-property)
- [App.json Properties](https://learn.microsoft.com/en-us/dynamics365/business-central/dev-itpro/developer/devenv-json-files)
- [NoImplicitWith Feature](https://learn.microsoft.com/en-us/dynamics365/business-central/dev-itpro/developer/devenv-deprecating-with-statements-overview)
- [Upgrade Codeunits](https://learn.microsoft.com/en-us/dynamics365/business-central/dev-itpro/developer/devenv-upgrading-extensions)
- [Upgrade Tags](https://learn.microsoft.com/en-us/dynamics365/business-central/dev-itpro/developer/devenv-upgrading-extensions#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`
