Skip to content
AITroveRead. Build. Understand.
Make this comfortable

Spring database contract phase: reject old writers only after backfill

Last updated: 30 Sept 20264 min read
tutorial
IntermediateBy AITrove Editorial

A contract migration enforces a required tenant column after data and application writers have moved to the new shape.

Download Spring source kit

The checked boundary

The test adds tenant_id as nullable, backfills the original row, then sets the column NOT NULL. An old writer that inserts only receipt_id and amount_minor now fails with a translated data-integrity exception. A new writer that supplies tenant_id=east succeeds. Two rows remain after the rejected insert.

That failure is the point of the test. The expansion phase lets both writer shapes coexist; the contract phase deliberately ends old-writer compatibility. Expansion and backfill must be verified before this step reaches a rolling deployment.

Gate on observed state

Before contraction, check that all running application versions write the new column, unresolved null count is zero, backfill ownership has been reviewed and rollback plans do not require the old writer. Keep the migration step in version control and test it on the target engine. H2 accepts this DDL, but its locks and failure modes are not a production model.

The local test starts from a new in-memory database each run. It does not prove that applying a migration twice is safe, that a mixed-version fleet has drained, or that a failed production DDL transaction rolls back. Migration tooling needs separate integration checks.

Checked source

sql
alter table shipment_receipt alter column tenant_id set not null;
-- Old insert omitting tenant_id now fails.
insert into shipment_receipt(receipt_id, amount_minor, tenant_id)
values (103, 900, 'east');

Verification boundary

SchemaEvolutionContractTest.contractRejectsOldWriterOnlyAfterBackfill. The excerpt is shortened or a labelled design sketch; the kit contains the checked tests.

Costs and limits

The test checks one H2 constraint transition. It does not run a mixed-version service fleet, a target database or a migration history runner.

Common Mistakes

  • Do not contract before old writers stop.
  • Do not assume failed production DDL rolls back as H2 does.
  • Do not skip the null-count and ownership checks.

Read next

Spring database rollout: add a nullable column before changing every writer, Spring database backfill: assign historical rows before enforcing ownership, Spring Boot migrations: what a versioned runner must add to the SQL test, Spring JdbcTemplate tenant predicates: put ownership in the SQL query.

spring
spring-boot
migration
Storage details