Skip to main content

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 DROP statements. This ensures that even an accidental command or typo can never lead to data loss. Use m migrate verify to 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/versions or app-specific migrations folders.
  • 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

  1. Alembic Patch: You generate a migration that renames the column physically in the database.
  2. Code Update: You update your DocType to use the new_name.
  3. The Handshake:
    • m migrate run executes first. The database now has new_name.
    • m migrate sync executes second. It detects a hash mismatch because the DocType has changed.
    • The engine reflects the database and sees that new_name already 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:

  1. Run Patches: Execute all version-controlled Alembic scripts (Evolution).
  2. Execute Hooks: Run any before_sync hooks for custom preparation.
  3. Final Sync: Run the declarative engine to ensure the schema perfectly matches the code (State).
  4. Execute Hooks: Run any after_sync hooks 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.