What role do Flyway/Liquibase play in a Spring Boot application, and why prefer them over Hibernate's ddl-auto in production?

9 minadvancedflywayliquibaseddl-auto

Quick Answer

Flyway and Liquibase are database migration tools: you write versioned migration scripts (SQL for Flyway, SQL/XML/YAML/JSON for Liquibase), and the tool tracks which migrations have already been applied to a given database, running only the new ones in a controlled, ordered, repeatable way — integrated into Spring Boot so migrations run automatically on application startup. This is strongly preferred over Hibernate's spring.jpa.hibernate.ddl-auto=update in production because ddl-auto's schema inference is unpredictable, can silently make destructive or unintended changes, and provides no history, rollback path, or review step for schema changes the way an explicit, version-controlled migration script does.

Detailed Answer

Hibernate's spring.jpa.hibernate.ddl-auto property (create, create-drop, update, validate, none) can automatically generate/adjust the database schema based on your @Entity classes — convenient for quick local development or prototyping, but strongly discouraged for production for several concrete reasons:

  • Unpredictable, inferred behavior: Hibernate infers what schema changes are "needed" by comparing your entity mappings against the current schema — this inference isn't always correct or safe, and its exact behavior can subtly change across Hibernate versions.
  • No history or audit trail: there's no record of what changed, when, or why — just whatever the schema happens to look like right now.
  • Risk of destructive changes: update can, in some scenarios, make changes that lose data (e.g., certain column type narrowings) without any confirmation step, and provides no rollback mechanism if something goes wrong.
  • No coordination across environments: there's no guaranteed way to ensure staging and production schemas evolved identically, since the "migration" isn't an explicit, reviewable artifact — it's just whatever Hibernate inferred at each startup.

Flyway and Liquibase solve this by treating schema changes as explicit, versioned migration scripts, checked into source control just like application code:

Flyway (SQL-first):

src/main/resources/db/migration/
├── V1__create_orders_table.sql
├── V2__add_status_column_to_orders.sql
└── V3__create_order_items_table.sql
-- V2__add_status_column_to_orders.sql
ALTER TABLE orders ADD COLUMN status VARCHAR(20) NOT NULL DEFAULT 'PENDING';

Liquibase supports SQL as well as XML/YAML/JSON changelog formats, offering somewhat more database-portability abstraction for teams that need to support multiple database vendors from one changelog.

Both tools maintain a metadata table in the target database itself (Flyway's flyway_schema_history, Liquibase's DATABASECHANGELOG) tracking exactly which migrations have already been applied — on startup, Spring Boot's auto-configuration for either tool automatically runs any new, not-yet-applied migrations in strict version order, and refuses to start if it detects an already-applied migration file has been modified (protecting against accidentally "un-syncing" what different environments believe has actually been applied).

Practical guidance: use ddl-auto=validate (or none) in any real environment beyond local development/prototyping — validate merely checks the schema matches your entities without changing anything, catching drift early — and let Flyway/Liquibase be the only thing that actually changes the production schema, via explicit, reviewed, version-controlled migration scripts.