Dual-Track Migration Philosophy
Framework M implements a Dual-Track Migration System designed to bridge the gap between rapid development (declarative) and rigorous production control (imperative).
The Dilemma: Frappe vs. Alembic
- The Frappe Way: Developers love the "it just works" experience of auto-syncing. You change a field in your DocType, and the database updates automatically. However, this can be dangerous in production if the engine makes a wrong assumption (like dropping a column you meant to rename).
- The Alembic Way: DBAs love the control of versioned patches. Every change is explicit, reviewed, and can be rolled back. However, writing a migration file for every single new field during development is slow and tedious.
The Framework M Solution: Dual-Track
We solve this by supporting both tracks simultaneously and providing a Unified Command that orchestrates them safely.
1. The Declarative Track (m migrate sync)
Driven by the DeclarativeSyncEngine, this track treats your DocType code as the "Source of Truth."
- Goal: Ensure the database physical state matches the code.
- Mechanism: Uses a deterministic hashing (Smart Sync) to quickly verify state and applies idempotent DDL changes.
- Safety (No-Drop Enforcement): This engine is physically incapable of dropping tables or columns. It contains no code to execute
DROPstatements. This ensures that even an accidental command or typo can never lead to data loss. Usem migrate verifyto see what could be pruned, but use the Imperative Track (Alembic) if you actually intend to delete data.
2. The Imperative Track (m migrate run)
Driven by Alembic, this track treats versioned files as the "Source of Truth."
- Goal: Execute a specific sequence of ordered transitions (patches).
- Mechanism: Standard versioned migration files stored in
alembic/versionsor app-specificmigrationsfolders. - Strength: Handles the "Difficult 20%" of schema evolution: renames, data transformations, and destructive pruning.
The "Self-Healing" Handshake
The magic of Framework M happens when these two tracks work together, particularly during complex changes like renaming a column.
Scenario: Renaming old_name to new_name
- Alembic Patch: You generate a migration that renames the column physically in the database.
- Code Update: You update your
DocTypeto use thenew_name. - The Handshake:
m migrate runexecutes first. The database now hasnew_name.m migrate syncexecutes second. It detects a hash mismatch because theDocTypehas changed.- The engine reflects the database and sees that
new_namealready exists. - The sync engine applies no DDL but updates the
__schema_versions__table with the new hash.
This process ensures that your "ordered patches" do the heavy lifting, while the "sync engine" validates the final state and updates the bookkeeping for the next fast-path run.
Unified Command: m migrate all
In most deployment scenarios, you should use m migrate all. This command enforces the correct sequence:
- Run Patches: Execute all version-controlled Alembic scripts (Evolution).
- Execute Hooks: Run any
before_synchooks for custom preparation. - Final Sync: Run the declarative engine to ensure the schema perfectly matches the code (State).
- Execute Hooks: Run any
after_synchooks for post-sync cleanup.
By following this order, Framework M provides the ultimate safety net: your patches get you from A to B, and the sync engine ensures you actually arrived at exactly B.