The problem
The previous state comes from a JSON snapshot this tool wrote into SequelizeMetaMigrations, not from the database. There is no introspection of the live schema anywhere in the diff path.
That works as long as this tool is the only thing that changes the schema. When something else does — a hand-written ALTER TABLE, a manually edited migration, an old sync({ alter: true }), a restore from a backup with different migration history, or another tool in the mix — the snapshot and the database drift apart, and nothing notices.
Why it matters
The failure is silent. Nothing errors, nothing warns; the next generated migration is simply wrong.
- A column added outside the tool is absent from the snapshot, so the next run emits
addColumn for it and the migration fails with "column already exists".
- A column dropped outside the tool stays in the snapshot, so every subsequent diff is computed on a false premise.
- Worst case: an unknown column is missing from a
changeColumn that redefines the table around it.
What detection would look like
makeMigration
→ describeTable() the tables in the snapshot
→ compare against the stored snapshot
→ on a mismatch: report what differs, and stop (or warn, per option)
Prisma catches this by replaying migration history into a shadow database. Drizzle Kit uses the same snapshot approach as this tool and has the same blind spot.
Why it is not a small change
describeTable() returns what the database stored, which is a different representation from the snapshot:
Sequelize.STRING(255) vs VARCHAR(255), and the spelling differs per dialect
Sequelize.BOOLEAN vs MySQL's TINYINT(1)
- a
false default coming back as "0" (observed in this repo's sqlite integration tests)
A useful comparison needs a per-dialect type normalization layer. Done carelessly it produces false positives on every run, which is worse than no check at all.
A smaller first step
Comparing only existence — tables and column names, no type comparison — catches most of the scenarios above with almost no false-positive risk. That may be worth shipping on its own before attempting type-level comparison.
Notes
- Documented as a known limitation in the README under "Limitations".
- Should be opt-out at minimum; the extra
describeTable() calls cost a round trip per table.
The problem
The previous state comes from a JSON snapshot this tool wrote into
SequelizeMetaMigrations, not from the database. There is no introspection of the live schema anywhere in the diff path.That works as long as this tool is the only thing that changes the schema. When something else does — a hand-written
ALTER TABLE, a manually edited migration, an oldsync({ alter: true }), a restore from a backup with different migration history, or another tool in the mix — the snapshot and the database drift apart, and nothing notices.Why it matters
The failure is silent. Nothing errors, nothing warns; the next generated migration is simply wrong.
addColumnfor it and the migration fails with "column already exists".changeColumnthat redefines the table around it.What detection would look like
Prisma catches this by replaying migration history into a shadow database. Drizzle Kit uses the same snapshot approach as this tool and has the same blind spot.
Why it is not a small change
describeTable()returns what the database stored, which is a different representation from the snapshot:Sequelize.STRING(255)vsVARCHAR(255), and the spelling differs per dialectSequelize.BOOLEANvs MySQL'sTINYINT(1)falsedefault coming back as"0"(observed in this repo's sqlite integration tests)A useful comparison needs a per-dialect type normalization layer. Done carelessly it produces false positives on every run, which is worse than no check at all.
A smaller first step
Comparing only existence — tables and column names, no type comparison — catches most of the scenarios above with almost no false-positive risk. That may be worth shipping on its own before attempting type-level comparison.
Notes
describeTable()calls cost a round trip per table.