Database Migrations
NextGenPoll uses Flyway for database schema management. All schema changes must go through versioned migration scripts — never modify the database schema directly in production.
Where Migrations Live
backend/src/main/resources/db/migration/
├── V1__Create_Initial_Schema.sql
├── V2__Seed_Data.sql
├── V3__Add_Response_Types.sql
└── ...
Naming Convention
V{version}__{Description}.sql
| Part | Rule |
|---|---|
V | Literal uppercase V |
{version} | Integer — must be higher than all existing versions |
__ | Two underscores |
{Description} | Words separated by _, no spaces |
Examples:
V8__Add_Image_Url_To_Questions.sqlV9__Create_Ai_Summaries_Table.sql
Once a migration has been applied to any environment, do not edit it. Flyway computes a checksum of each script — modifying an applied script will cause the next startup to fail with a checksum mismatch error. If you need to correct something, add a new migration.
How Migrations Run
Local Development
Flyway runs automatically on backend startup via the Spring Boot Flyway auto-configuration. On first run:
- Creates the
flyway_schema_historytable. - Applies all pending migration scripts in version order.
To run migrations manually without starting the backend:
# Windows
cd backend
mvnw.cmd flyway:migrate
# Mac / Linux
cd backend
./mvnw flyway:migrate
CI (Testing)
CI uses an H2 in-memory database with the test Spring profile. Flyway is skipped in CI (-Dflyway.skip=true) because H2 uses JPA DDL auto-generation (ddl-auto=create-drop) instead.
# backend-ci.yml
./mvnw -B verify -Dflyway.skip=true
Production (CD)
The backend-cd.yml workflow runs Flyway against the Azure PostgreSQL database before deploying the new JAR:
./mvnw flyway:repair flyway:migrate -B \
"-Dflyway.url=${{ secrets.AZURE_DB_URL }}" \
"-Dflyway.user=${{ secrets.AZURE_DB_USERNAME }}" \
"-Dflyway.password=${{ secrets.AZURE_DB_PASSWORD }}" \
-Dflyway.skip=false \
-Dflyway.baselineOnMigrate=true \
-Dflyway.outOfOrder=true
flyway:repair is run first to clean up failed or checksum-mismatched migrations before applying new ones.
Adding a New Migration
- Determine the next version number (check the highest
V{n}indb/migration/). - Create the file:
backend/src/main/resources/db/migration/V{n+1}__{Description}.sql
- Write your SQL. Use standard PostgreSQL syntax. Wrap multi-statement migrations in a transaction if applicable:
-- V8__Add_Image_Url_To_Questions.sqlALTER TABLE questions ADD COLUMN image_url VARCHAR(500);
- Restart the backend — Flyway will apply the new migration automatically.
- Update the corresponding JPA entity class and repository if needed.
Flyway Troubleshooting
Checksum Mismatch
Error: Migration V3 failed… checksum mismatch
Cause: An applied migration file was modified.
Fix:
./mvnw flyway:repair
This removes the failed entry from flyway_schema_history. Then restore the original file content, add a new corrective migration, and rerun.
Out-of-Order Migrations
Error: Migration V7 is not applied
Cause: A migration with a lower version number was added after higher versions were already applied (common in feature branches).
Fix: The CD pipeline uses -Dflyway.outOfOrder=true. For local dev, add this to application-dev.yml:
spring:
flyway:
out-of-order: true
Baseline on Migrate
If deploying to a database that already has tables but no flyway_schema_history (e.g., a pre-Flyway database), Flyway will fail. The CD pipeline uses -Dflyway.baselineOnMigrate=true to handle this automatically.
Schema Drift
If spring.jpa.hibernate.ddl-auto=validate is set (used in prod profile), the application will fail to start if the JPA entity definitions do not match the actual database schema. This is intentional — it catches missing migrations early. Fix by adding the missing migration.