migration-path — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited migration-path (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
Changing something with existing consumers is a migration, not an edit. Big-bang cutovers break callers you didn't know existed — mobile apps on old versions, scripts, partner APIs, queued messages, and "temporary" integrations that became permanent.
Move in steps that keep old and new working together until everyone has moved, then contract (remove the old). Each step should be independently shippable and as reversible as your data model allows.
Pairs with [[interface-design]] for contract evolution, [[data-modeling]] for schema and backfills, [[launch-readiness]] for deploy order and rollback limits, [[incremental-delivery]] for slice-sized migration steps, [[dependency-hygiene]] for breaking dependency upgrades, [[git-flow]] for staged commits, and [[decision-docs]] for deprecation timelines and ADRs.
your contracts or callers are affected
Skip for purely internal code with no external callers and no persisted data depending on the old shape — still document if the "internal" surface is wide. Skip one-off data fixes on tables with no live readers if truly isolated.
Work in order. Inventory before edit.
You can't migrate safely without knowing who breaks when you change:
| Source | What to find |
|---|---|
| Codebase | grep, importers, OpenAPI clients, shared packages |
| Repos you don't own | Mobile apps, partner integrations, data pipelines |
| Logs / metrics | Traffic on endpoint, field usage, event consumers |
| Queues / topics | Downstream subscribers, replay tools |
| Config | Env vars read by other services or deploy scripts |
| Docs / runbooks | External references to old names |
Unknown consumers are the ones that break at 2am. Search aggressively; ask teams; check API analytics. If you can't find callers, assume they exist and keep compatibility longer.
Document the dependency inventory in the PR or migration doc — who, what version, owner contact.
The default pattern for schema and contracts ([[data-modeling]], [[interface-design]]):
EXPAND — add new alongside old (nullable column, new endpoint, dual field)
MIGRATE — move callers and data to new path (backfill, client updates, dual-read/write)
CONTRACT — remove old only when usage is zero (drop column, delete endpoint, remove field)Each phase is a separate deploy when possible ([[incremental-delivery]], [[launch-readiness]]).
Never contract in the same release as expand unless traffic is truly zero.
| Layer | Expand | Migrate | Contract |
|---|---|---|---|
| DB column | Add new_col nullable | Backfill; app writes both | Drop old_col |
| API field | Add emailAddress; keep email | Clients read new; dual-write | Remove email |
| Endpoint | Ship /v2/orders | Route traffic; deprecate v1 | Remove v1 at zero traffic |
| Event | New schemaVersion: 2 optional fields | Consumers handle both | Stop v1 publish |
| Config | New key with default from old | Services read new | Remove old key |
Pick strategies that match risk and duration:
| Strategy | When | Notes |
|---|---|---|
| Dual-write | Rename/move data | Write old + new; verify parity |
| Dual-read | Prefer new, fallback old | Until backfill complete |
| Shim / adapter | Old API surface must persist | Translate at boundary |
| API versioning | External clients slow to update | /v1 + /v2 coexist |
| Feature flag | Behavior switch | Off = old path ([[launch-readiness]]) |
| Shadow write | Test new path | Write new without reading yet |
Compatibility window — explicit end date or usage threshold, not "eventually."
Old and new must coexist in production without requiring simultaneous deploy of all services unless you control every caller and can coordinate a maintenance window.
Data migrations are where rollbacks die ([[data-modeling]]):
Rules:
WHERE new_col IS NULL LIMIT n)-- Pattern: repeated until zero rows updated
UPDATE orders SET new_status = map_old_status(old_status)
WHERE new_status IS NULL
LIMIT 5000;Destructive transforms (delete, narrow type) — extra caution; often copy to new table then swap, not in-place mutate.
Document data rollback: often forward-fix only — design expand-contract so contract happens only after confidence.
Wrong order = outage mid-deploy ([[launch-readiness]]):
Typical safe sequence:
1. Deploy migration EXPAND (backward compatible)
2. Deploy app that reads/writes BOTH or new-only with nullable expand
3. Run backfill job; monitor
4. Deploy app that reads NEW only (still writes both if dual-write)
5. Deploy app write NEW only
6. Later: CONTRACT migration (drop old) — separate releaseMobile / external clients — server must stay backward-compatible until app store adoption catches up.
Document in release notes: "Requires migration 0042 before app 2.5.0."
Before removal:
Deprecation header, sunset date, Sunset RFC 8594 where applicableDeprecation: true
Sunset: Sat, 01 Jan 2027 00:00:00 GMT
Link: </v2/orders>; rel="successor-version"Minimum deprecation window — proportional to client control (internal week, external quarter+).
Record in [[decision-docs]] when policy affects many teams.
Remove old path when:
Contract PR is intentionally boring — delete dead code, drop column, remove flag default old path.
| Change | Code rollback | Data rollback |
|---|---|---|
| Feature flag OFF | Easy | N/A |
| Revert deploy | Easy if no migration | Old code may not match new data |
| Expand migration | N/A | Usually harmless to leave extra column |
| Backfill | N/A | Often not reversible — plan forward fix |
| Contract (drop column) | Hard | Very hard — avoid until certain |
Test rollback in staging before high-tier launch ([[launch-readiness]]):
Rename database column
Add new_name → dual-write in app → backfill → read new → write new only → drop old_name.
Add required field to API
Phase 1: optional in API + DB nullable → clients updated → Phase 2: required in API → Phase 3: DB NOT NULL after backfill.
Split monolithic table
New table + dual-write → backfill historical → migrate reads → stop writes to old → archive old table.
API v2 for external partners
v2 live with v1 → partner migration deadline → metric on v1 → sunset headers → remove v1.
Event schema v2
Publish v2 events with schemaVersion; consumers dual-handle → migrate consumers → stop v1 publish → retire old consumer code.
Breaking dependency major ([[dependency-hygiene]])
Inventory imports → upgrade in branch slices → compat shim if needed → remove shim after callers updated.
Extract service from monolith
Define API matching old in-process calls → route one caller at a time → strangle monolith module → delete in-process path when zero.
Feature deprecation (UI + API)
Flag default off → metric on usage → remove UI → API deprecated headers → remove API when zero.
Config key rename
Read new with fallback to old → deploy all readers → deploy writers to new only → remove old key from config store.
Emergency data repair
Stop harmful writes → scope rows affected → idempotent repair script in batches → verify counts → postmortem + guardrail ([[incident-response]]).
~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.