Edit a SQLAlchemy model, run make-migrations, and the tool writes a versioned SQL file with upgrade and rollback sections beside each other. The file is what gets reviewed in the pull request and committed to source control; the plan that produced it is written alongside as .plan.json, machine-readable metadata for CI and debugging that migrate never executes.
Generation diffs the model state against the latest schema snapshot, falling back to the live database when no snapshot exists yet. The diff engine emits typed operations, and the SQL is derived from those operations, so the same models and the same state always produce the same SQL. Names are auto-derived from the change, or you pass a description like "add bio".
The migration file is derived from the models. Old files stay useful for review and deployment, but they don't define the schema anymore; the next generation run exposes any disagreement.
class User(Base):
__tablename__ = "users"
email = Column(String(255), unique=True, nullable=False)
bio = Column(Text, nullable=True)-- upgrade
ALTER TABLE users ADD COLUMN bio TEXT;
-- rollback
ALTER TABLE users DROP COLUMN bio;Where does make-migrations write the file?
To migrations/<database_name>/, named {database_name}__{version}_{description}.sql. Each database keeps its own linear versioned sequence.
How are renames detected?
When a column is dropped and a new column of the same type is added, dbwarden detects a potential rename and emits ALTER TABLE ... RENAME COLUMN instead of a drop-and-create. Ambiguous cases can be declared with --rename.
Can I preview the migration before writing it?
Yes: make-migrations --plan prints the migration plan JSON without writing any files.