-
Determine next version number
ls <module>/src/main/resources/db/migration/V*.sql | sort -t V -k2 -n | tail -1
Increment by 1. Check git for pending migrations from other branches.
-
Check for version conflicts
git fetch origin && git diff origin/main --name-only | grep "db/migration/V"
If conflict exists, take the next available version.
-
Plan before writing: state what SQL will be written, whether it is backward-compatible,
and whether a data backfill is needed (always a separate subsequent migration).
-
Write the file — V<n>__<snake_case_description>.sql in <module>/src/main/resources/db/migration/
- New table: include audit columns (
created_at, updated_at, deleted_at)
- New column:
ADD COLUMN IF NOT EXISTS ... NULL or with DEFAULT (backward compat)
- New index: always in a separate migration; use
CREATE INDEX CONCURRENTLY IF NOT EXISTS
- Guards: always
IF NOT EXISTS / IF EXISTS on every DDL statement
-
Validate: ./mvnw flyway:validate
-
Migrate: ./mvnw flyway:migrate — confirm Success in flyway:info
-
Full verify: ./mvnw clean verify
Hibernate validate confirms schema matches entity mappings.
-
Backfill (if needed): write V<n+1>__backfill_<desc>.sql separately.
Use batched updates (WHERE id > $last LIMIT 1000) to avoid lock contention.