AgentSkillsCN

Migrations Data Model Changes

迁移数据模型变更。

SKILL.md

Skill: migrations-data-model-changes

You are performing data model and migration work for a Django + Postgres application.

This skill standardizes how to change schemas safely, how to backfill data, and how to avoid outages or irreversible migrations.

When to use this skill

Use this skill whenever you:

  • Add/change/remove a Django model field
  • Add/change constraints (unique, not-null, foreign keys)
  • Add/change indexes
  • Backfill/transform data
  • Change auth/user-related schema
  • Need to evaluate migration risk, downtime, or rollback strategy

Inputs

  • The approved design: docs/features/<feature-slug>/02-design.md
  • Current models + migrations in the repo
  • Current database assumptions (table sizes, traffic, downtime tolerance), if known

If traffic/size is unknown, assume moderate traffic and design for low-risk / minimal locking changes.

Outputs

Design phase outputs

In 02-design.md, include a Data model & migrations section that covers:

  • schema changes (tables/fields/indexes/constraints)
  • migration strategy (expand/contract if needed)
  • backfill approach (if any)
  • rollback strategy
  • operational risk notes (locks, long-running migrations)

Implementation phase outputs

  • Django migration files that implement the plan
  • Updated docs/features/<feature-slug>/03-implementation-notes.md with:
    • migration status (created/applied/deferred)
    • exact commands to run
    • any special operational notes

Hard rules

  • Do NOT bypass migrations (no manual DB edits as a substitute).
  • Do NOT introduce irreversible or high-downtime migrations without explicitly documenting the risk and proposing safer alternatives.
  • Do NOT ship schema changes that break backward compatibility unless the design explicitly calls for it and the deployment plan addresses it.
  • Any custom User model is authoritative and must not be replaced.

Safety model: prefer Expand/Contract

When changes could break existing code or require long operations, use the expand/contract pattern:

  1. Expand (backward compatible)
  • Add new nullable columns / tables
  • Add new code paths that write both old+new (dual write) or write new while reading old
  • Add feature flags if needed
  1. Backfill (safe + resumable)
  • Populate new columns incrementally
  • Avoid one giant transaction for large tables
  1. Switch
  • Update reads to use new column/table
  • Keep dual-write temporarily if needed
  1. Contract
  • Remove old columns/constraints only after the switch is proven

Common safe patterns (Django/Postgres)

Adding a new field

Prefer:

  • add field as null=True (or with safe default behavior in code)
  • deploy code that handles nulls
  • backfill
  • then make it null=False and add constraints

Avoid:

  • adding a NOT NULL field with a default on a large table in one step (can lock/rewrite)

Adding a unique constraint

Prefer:

  • backfill + deduplicate first
  • add a unique index/constraint after data is clean
  • ensure application validation matches DB constraint

Renaming a field

Prefer expand/contract:

  • add new field
  • dual write / copy data
  • switch reads
  • drop old field

Direct rename may be okay on small tables, but can break older code during rolling deploys.

Adding an index

  • Consider index build time and locking.
  • For large tables, prefer concurrent index creation (requires special handling in Django).
    • If using CREATE INDEX CONCURRENTLY, the migration must typically set atomic = False.

Foreign keys

  • If adding an FK to existing data:
    • add column nullable
    • backfill valid references
    • validate data
    • then enforce NOT NULL / constraints

Data migrations & backfills

Principles

  • Backfills must be idempotent and ideally resumable.
  • Avoid loading entire tables into memory.
  • Prefer chunked updates.

Recommended approaches

  • Use a Django RunPython data migration for small/medium datasets.
  • For large datasets, prefer a management command or background job (documented in deploy notes), then do a later “contract” migration.

Don’t do this

  • Don’t run long-running backfills inside a single transaction if it risks timeouts or lock contention.
  • Don’t assume production data is clean.

Transactionality & locking

  • Django migrations are atomic by default (transactional). That’s safer for small changes.
  • For operations that cannot run in a transaction (e.g., some concurrent index operations), explicitly set atomic = False and document why.
  • Always call out potential locks in the design and deployment notes.

Backward compatibility checklist

Before finishing design/implementation, confirm:

  • Old code can run against the new schema (during rolling deploy)
  • New code can run against the old schema (if deployments are staged)
  • API contracts remain valid (or versioned)

Rollback strategy

Every non-trivial schema change must include a rollback plan:

  • Can we roll back app code without rolling back DB?
  • If DB rollback is required, is it safe and tested?
  • If irreversible, document explicitly and add mitigation (feature flag, staged rollout)

Verification expectations

Provide (at minimum):

  • migration generation commands (e.g., python manage.py makemigrations)
  • migration apply commands (e.g., python manage.py migrate)
  • smoke checks that touch the modified models/endpoints

Do not claim migrations were applied unless you ran them.