What role do Flyway/Liquibase play in a Spring Boot application, and why prefer them over Hibernate's ddl-auto in production?
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:
updatecan, 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.