← Software design practices guides

How to define interfaces before implementation

How-To Software design practices Intermediate 1101007HOWTO-1101007

HOWTO-1101007Software design practicesIntermediate

This guide shows you how to define interfaces before implementation with SpecDD in a spec-driven development workflow.

An interface can be an API route, CLI command, service method, package export, component prop contract, event payload, or adapter boundary. Defining it before implementation helps teams agree on the promise callers will depend on before the code creates accidental commitments.

SpecDD is useful here because interface contracts are written in the same source-adjacent language as the rest of the system. The spec can define what is exposed, what is accepted, what is returned, what errors are raised, and what must not leak through the interface.

Short answer

Write a local spec for the interface boundary before implementing it. Use Exposes for public entry points, Accepts for inputs, Returns for outputs, Raises for errors or signals, Must for stable behavior, Must not for non-goals and implementation leakage, Example or Scenario for concrete cases, and Done when for contract checks.

When to use this guide

Use this guide when:

The design idea

Implementation-first development often creates accidental interfaces. A helper function becomes public because another file imported it. An API response leaks a database shape because it was easy to serialize. A component prop carries extra state because the caller happened to have it.

Interface-first SpecDD reverses that pressure. The spec states the interface contract first. Implementation can then change internally as long as it satisfies the contract and respects the boundaries.

Steps

1. Choose the interface boundary

Pick the boundary callers will use:

Spec: Create Trip API

or:

Spec: Trip Summary Service

or:

Spec: Destination Search Adapter

If the interface is public to another package, service, team, or user, make the contract especially explicit.

2. Name exposed entry points

Use Exposes:

Exposes:
  POST /trips

For code interfaces:

Exposes:
  createTrip
  validateItineraryItem

For CLI:

Exposes:
  trip export

Only list entry points callers are meant to use.

3. Define inputs and outputs

Use Accepts and Returns:

Accepts:
  CreateTripRequest with name and destination

Returns:
  201 with TripResponse
  400 for validation failure

For a service:

Accepts:
  trip id
  itinerary item input

Returns:
  accepted itinerary item
  validation result for rejected input

The goal is to define the caller-visible contract, not every internal field.

4. Specify errors and edge cases

Use Raises for stable errors, events, or signals:

Raises:
  TripValidationError when required fields are missing
  TripStorageUnavailable when storage cannot be reached

Use Scenario for important behavior:

Scenario: missing destination
  Given the request has no destination
  When the create trip API is called
  Then the API returns a validation failure
  And no trip is stored

This keeps error behavior from being invented later by implementation convenience.

5. Add implementation boundaries

Use Must not to protect the interface:

Must not:
  Expose internal database field names in API responses.
  Require clients to provide server-generated ids.
  Bypass trip storage.

Use Forbids if a dependency or access pattern is blocked:

Forbids:
  direct booking API access

These rules prevent the interface from leaking implementation or reaching across unrelated boundaries.

6. Review the contract before coding

Before implementation, review:

If this review reveals ambiguity, fix the spec before implementation hardens the wrong contract.

Full example

Spec: Create Trip API

Purpose:
  Accept requests to create trips.

Exposes:
  POST /trips

Accepts:
  CreateTripRequest with name and destination

Returns:
  201 with TripResponse
  400 for validation failure
  500 for storage failure

Raises:
  TripCreated event after a trip is stored

Must:
  A trip name is required.
  A destination is required.
  Missing required fields return a clear validation error.

Must not:
  Create itinerary items.
  Bypass trip storage.
  Purchase bookings or tickets.
  Expose internal storage keys in TripResponse.

Scenario: missing destination
  Given the request has no destination
  When the create trip API is called
  Then the API returns a validation failure
  And no trip is stored

Done when:
  Request validation behavior is covered by API checks.
  TripResponse contains only public response fields.

The spec is detailed enough for implementation, tests, and caller review, but it does not prescribe internal helper structure.

Common mistakes

How to verify the result

The interface is ready for implementation when:

← Software design practices guides