schema-change
Evolve SQLAlchemy models, constraints, indexes, Alembic revisions or data transformations. Use when a change adds or alters tables, columns, relationships, migrations or stored data. DTO, Pydantic or
Files9
View on GitHubSKILL.md.claude/skills/schema-change/SKILL.md
GitHubschema-change
Read the project profile, affected data criteria, central ORM models
(backend/src/common/database/models/) and the Alembic graph. Coordinate with
backend-change and consumers when HTTP contracts change.
Steps
- Generate with
just backend new-migration "description"when appropriate, then deliberately review SQL, FK ordering, constraints and data transforms. Merge conflicting revisions through Alembic's graph workflow. - Test an empty database and the supported previous revision with
representative data. Use
just backend check-migrations; it owns disposable databases, upgrades and drift checks. Add data-preservation assertions when a migration transforms existing values. - Confirm an unambiguous integrated head, model parity and preserved data. Plan compatible deployment phases when API, frontend and schema do not update simultaneously. When the change reaches DTOs or routes, refresh the API snapshot with openapi-sync. Place shared domain models as backend-architecture describes.
- Record the structural decision in an ADR and the technical evidence on the exact integrated revision. Return findings to the implementer; validate-change checks evolution and compatibility acceptance. Clean only owned resources.
Gotchas
- Autogeneration is a starting point, not a reviewed migration.
metadata.create_alland migrate-current are not migration evidence.- Do not require a destructive downgrade by default.
- Run
just backend migratebeforejust backend new-migration; autogenerate diffs against the local database, so a stale database yields a wrong revision. Base.metadatahas nonaming_convention: name new constraints and indexes explicitly (name="uq_<table>_<cols>",ix_...,fk_...) so later revisions can drop them deterministically.- Revision ids follow
YYYYMMDD_NNNNNN(date plus a global sequence, e.g.20260907_000002) with files named<revision>_<slug>.pyinbackend/src/common/database/versions/. The recipe'sfile_templateproduces a timestamped name with a random hex id, so setrevisionand rename the file to the next sequence before review. env.pyinclude_objectskipsdirectus_*tables; never write migrations for them, and do not read their absence from autogenerate as drift.