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. It's 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 (like 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. Let Flyway/Liquibase be the only thing that actually changes the production schema, via explicit, reviewed, version-controlled migration scripts.