← Spec-writing technique guides

How to write specs for backwards compatibility

How-To Spec-writing technique Intermediate 1071013HOWTO-1071013

HOWTO-1071013Spec-writing techniqueIntermediate

This guide shows you how to write SpecDD specs for backwards compatibility in a spec-driven development workflow.

Compatibility rules matter when other code, users, clients, scripts, data, or teams rely on existing behavior. A good compatibility spec makes the stable contract visible so later changes do not break it accidentally.

Short answer

Write compatibility constraints at the level that owns the public contract. Use Exposes, Accepts, Returns, Raises, Must, Must not, Example, Scenario, and Done when to describe what must remain stable, what may change, what is deprecated, and how compatibility is checked.

When to use this guide

Use this guide for:

Steps

1. Identify the stable contract

Ask what callers or users rely on:

Place the rule in the spec that owns that contract.

2. Name compatible behavior

For an API:

Spec: Trip itinerary API

Exposes:
  GET /api/trips/:tripId/itinerary

Returns:
  200 with itinerary items ordered by day
  404 when the trip does not exist

Must:
  Continue returning the item id, day, placeName, and notes fields for existing clients.

For a CLI:

Spec: Export command

Exposes:
  trip export

Must:
  Keep --format json as a supported flag.
  Preserve the top-level trips array in JSON output.

3. Forbid breaking changes

Use Must not for breakage that looks tempting during refactors:

Must not:
  Rename placeName in version 1 responses.
  Remove --format json before a replacement is released.
  Change the default export sort order.

Compatibility specs should make unsafe changes obvious in review.

4. Specify versioning or deprecation

If the contract can evolve, say how:

Must:
  Add new response fields in a backwards-compatible way.
  Keep deprecated fields available until the v2 itinerary API is released.

Must not:
  Require existing clients to send newly added optional fields.

For deprecation work, keep tasks local and checkable:

Tasks:
  [ ] Add a deprecation warning for legacy export output.
  [ ] Document the replacement output format.

Done when:
  Existing JSON export tests still pass.
  Deprecated output includes the warning.

5. Cover migrations and data contracts

For persisted data, compatibility often means old data can still be read:

Spec: Trip note migration

Must:
  Read trip notes saved before the note metadata migration.
  Preserve note text during migration.

Must not:
  Drop unknown note metadata keys during migration.

Scenario: Legacy trip note
  Given a trip note without metadata
  When the migration runs
  Then the note text is preserved
  And default metadata is added

This kind of spec protects migration behavior from later cleanup work.

6. Require compatibility checks

Use Done when to require tests, snapshots, contract checks, or manual verification:

Done when:
  Existing v1 response contract checks pass.
  Legacy fixture data can still be read.
  CLI JSON output preserves the documented top-level shape.

Avoid vague criteria like “compatibility is maintained” unless the project has a specific command or process that defines it.

Common compatibility targets

Common mistakes

How to verify the result

The compatibility spec is ready when:

← Spec-writing technique guides