dotnet-migrate
Installation
SKILL.md
Safe migration workflow (.NET)
Migrations are where a working codebase quietly acquires risk: a column drop that loses data, a framework bump that breaks at runtime not compile time, a transitive package that shifts behavior under you. The defense is the same four rules in every flow below.
- Preview before you apply. Read the generated SQL, the breaking-change list, the changelog - never run a step blind.
- Carry a rollback. Know the exact command or commit that undoes the step before you take it.
- Re-verify after every step. Build and run the tests; a green pre-flight that you never re-check proves nothing.
- One logical change per step. A migration, an upgrade, a bump - keep them atomic so a break bisects cleanly.
Assess blast radius with serena (find_symbol, find_referencing_symbols) or the LSP. Do not Read whole files hunting for who touches a type - that is exactly the work the symbol tools do faster.
Flow A - EF Core schema migration
- See where you are.
dotnet ef migrations listshows what is applied versus pending. Use serena to find the entities you are about to change and everything that references them. - Generate one named migration.
dotnet ef migrations add <Name>- name it Verb-then-subject so the history reads as a log:AddOrderShippedAt,MakeEmailUnique,DropLegacyStatus. The one-change-per-migration discipline itself isdatabase-conventions's to state; this step is just the EF naming and generation mechanics. - Preview the SQL.
dotnet ef migrations script --idempotent(or--idempotent <from> <to>for a range). Read it for the dangerous shapes: dropped or renamed columns, a non-nullable add with no default, a type narrowing that truncates, an index or constraint added to a large table under a lock. The--idempotentflag guards each step with an__EFMigrationsHistorycheck so the script is safe to run against a database at any applied state, and re-running it is a no-op. EF cannot see your data - you have to. - Stage destructive change in two deploys. Anything that can lose data or that the old code still depends on is expand-then-contract: first add the new column and backfill (the old code keeps working), ship, then in a later migration drop the old column once nothing reads it. Never collapse both halves into one migration against a live database. A wide backfill is a data migration, not a schema one - run it in batches outside the
ALTER, never as a singleUPDATEunder a table lock. - Apply and verify. In dev,
dotnet ef database update, then build and run the tests. For anything past a local box, do not apply from a developer machine or callDatabase.Migrate()on app start under load (it serializes startup and needs schema-owner rights at runtime). Build a migrations bundle once -dotnet ef migrations bundle -o efbundle(add--self-contained -r <rid>to fold the runtime in too) - and run that single-file executable as a gated deploy step. Likedotnet ef database updateit checks__EFMigrationsHistoryand applies only the migrations still missing, so it needs neither the SDK, the EF tool, nor the project source on the target and is the zero-downtime artifact. - Know the undo. Roll the database back with
dotnet ef database update <PreviousMigration>, then delete the migration files withdotnet ef migrations remove(which un-snapshots cleanly - never delete the files by hand). Never hand-edit an already-applied migration - add a new one. Once a migration has shipped to any shared environment its rollback is a new forward migration, not aremove. Query, tracking, and configuration mechanics live indotnet-data-access.