Install
$ agentstack add skill-int2t05-engineering-skills-deprecation-migration ✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ✓ Network access No
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
Verified badge
Passed review? Show it. Paste this badge into your README, it links to the public security report.
Reliability & compatibility
Declared compatibility
Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.
We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.
How agent discovery & health will work →About
Deprecation and Migration
Code is a liability, not an asset. Every line carries maintenance cost — bugs, dependency updates, security patches, onboarding overhead. Deprecation is the discipline of removing code that no longer earns its keep; migration is the process of moving users safely from old to new. Most organizations build things well; few remove them well.
Hyrum's Law makes removal hard: with enough users, every observable behavior becomes depended on — including bugs, timing quirks, and undocumented side effects. Deprecation therefore requires active migration, not just announcement. Plan removal at design time — systems with clean interfaces, feature flags, and minimal surface area are far easier to sunset.
When to use
- Replacing an old system, API, or library with a new one.
- Sunsetting a feature that's no longer needed, or consolidating duplicate implementations.
- Removing dead code that nobody owns but everybody depends on.
- Planning the lifecycle of a new system (deprecation planning starts at design time).
- Deciding whether to maintain a legacy system or invest in migration.
- Upgrading a dependency or framework version, including major/breaking upgrades (see
references/dependency-upgrade.md). - Moving data between systems (dual-write, backfill, CDC, cutover) — see
references/data-migration.md.
Not for: patching a security vulnerability in place (use security-review); routine commits (use git-workflow).
Steps
1. Make the deprecation decision
Before deprecating anything, answer:
- Does this system still provide unique value? If yes, maintain it.
- How many consumers depend on it? Quantify the migration scope.
- Does a replacement exist? If no, build the replacement first — never deprecate without an alternative.
- What's the migration cost for each consumer? Trivially automated → do it; manual and high-effort → weigh against maintenance cost.
- What's the ongoing cost of not deprecating? Security risk, engineer time, opportunity cost of complexity.
Zombie-code diagnostic — if the target shows these signs, it's zombie code: nobody owns it, but everybody depends on it.
- No commits in 6+ months; no assigned maintainer or team
- Failing tests that nobody fixes
- Dependencies with known vulnerabilities that nobody updates
- Documentation referencing systems that no longer exist
Response: assign an owner and maintain it properly, or deprecate it with a concrete migration plan. Zombie code cannot stay in limbo — it either gets investment or removal.
2. Choose advisory vs compulsory
| Type | When | Mechanism | |------|------|-----------| | Advisory | Migration optional; old system stable | Warnings, docs, nudges. Users migrate on their own timeline. | | Compulsory | Security issues, blocks progress, or maintenance unsustainable | Hard deadline; removal by date X. Migration tooling must be provided. |
Default to advisory. Use compulsory only when the cost or risk justifies forcing migration — and never just announce a deadline; provide tooling, documentation, and support (the Churn Rule: if you own the infrastructure being deprecated, you migrate your users, or provide backward-compatible updates that require no migration).
3. Migrate incrementally
- Build the replacement — covers all critical use cases, documented, proven in production (not "theoretically better").
- Announce and document — deprecation notice with status, replacement, removal date, reason, and a migration guide with concrete steps and examples.
- Migrate consumers one at a time — identify touchpoints, update to the replacement, verify behavior matches (tests, integration checks), remove old references, confirm no regressions.
- Remove the old system — only after all consumers migrated. Verify zero active usage (metrics, logs, dependency analysis), then remove code, tests, docs, config, and the deprecation notices. Removing code is an achievement.
4. Pick a migration pattern
Strangler — run old and new in parallel; route traffic incrementally (0% → 10% canary → 50% → 100%); remove the old system when it handles 0%.
Adapter — translate calls from the old interface to the new implementation. Consumers keep using the old interface while you migrate the backend.
class LegacyTaskService implements OldTaskAPI {
constructor(private newService: NewTaskService) {}
getTask(id: number): OldTask {
return this.toOldFormat(this.newService.findById(String(id)));
}
}
Feature flag — switch consumers one at a time:
function getTaskService(userId: string): TaskService {
if (featureFlags.isEnabled('new-task-service', { userId })) {
return new NewTaskService();
}
return new LegacyTaskService();
}
Concrete example — migrating test helpers to @total-typescript/shoehorn. When the migration target is a library whose API replaces a problematic pattern (e.g. as type assertions in tests), the same incremental process applies:
- Install the replacement:
npm i @total-typescript/shoehorn. - Find call sites:
grep -rE ' as [A-Z]' --include='*.test.ts' --include='*.spec.ts'. - Replace
as Type→fromPartial(...)(partial data that still type-checks); replaceas unknown as Type→fromAny(...)(intentionally wrong data for error tests). UsefromExact()to force a full object when you plan to swap tofromPartiallater. - Add imports from
@total-typescript/shoehorn, run typecheck, verify.
Test code only — never use shoehorn in production code.
Database schema (expand/contract) — the riskiest migration because data is the one thing you can't roll back by reverting a deploy. Never change a column in place. Migrate in additive phases so old and new code are both valid at every step:
EXPAND ──────→ MIGRATE ──────→ CONTRACT
add new backfill rows, once no code reads
column dual-write the old column,
(nullable) old+new drop it in a later,
from app separate deploy
Worked example — renaming name to full_name:
- Expand. Add
full_namenullable. Deploy. (Old code ignores it.) - Dual-write. App writes both
nameandfull_nameon every insert/update. Deploy. - Backfill. Copy
name → full_namefor existing rows, in throttled batches (don't lock the table). - Switch reads. Point the app at
full_name, keep writing both. Deploy and bake. - Contract. Stop writing
name; in a separate, later deploy, drop the column.
Rules: additive first, destructive last and alone; every migration has a tested down path; backfill in batches off the hot path; build large indexes without blocking writes (e.g. Postgres CREATE INDEX CONCURRENTLY); decouple cutover from code by feature flag when risky.
> Warning: If CONCURRENTLY fails mid-build, it leaves an INVALID index that silently does nothing — check \di or pg_indexes for validity; drop and recreate before relying on it.
Verify
After completing a deprecation:
- [ ] Replacement is production-proven and covers all critical use cases
- [ ] Migration guide exists with concrete steps and examples
- [ ] All active consumers migrated (verified by metrics/logs)
- [ ] Old code, tests, documentation, and config fully removed
- [ ] No references to the deprecated system remain in the codebase
- [ ] Deprecation notices removed (they served their purpose)
After a database schema migration:
- [ ] Change ships in additive phases (expand → backfill → contract), not a single in-place edit
- [ ] Old and new code are both valid against the schema at every deploy step
- [ ] Each migration has a tested down path; backfills run in throttled batches
- [ ] Destructive steps (drop/rename) ship in their own deploy after no code references the old shape
References
- [${CLAUDEPLUGINROOT}/references/engineering-principles.md](${CLAUDEPLUGINROOT}/references/engineering-principles.md) — shared discipline (verify don't assume, surgical scope, simplicity)
- [references/dependency-upgrade.md](references/dependency-upgrade.md) — changelog/migration-guide reading, breaking-change triage, branch-based upgrade, root-cause fix per break
- [references/data-migration.md](references/data-migration.md) — moving data between systems: dual-write + backfill, CDC, cutover, verification, failure modes
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: int2t05
- Source: int2t05/engineering-skills
- License: MIT
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet, be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.