Post

Contract-First vs Code-First API Development

Contract-First vs Code-First API Development

APIs are long-lived contracts between teams. The main decision is whether the contract is defined first (contract-first) or inferred from code (code-first). The right choice depends on how many consumers you have and how quickly the API evolves.

Contract-First Development

Contract-first means designing the API in OpenAPI or another schema language before writing server code.

Strengths:

  • Clear agreement between producers and consumers.
  • Enables early mock servers and client generation.
  • Encourages backward compatibility reviews.

Weaknesses:

  • Requires rigorous governance.
  • Schema drift if code is changed without updating the contract.

Code-First Development

Code-first means writing the controller code first and generating the schema from annotations.

Strengths:

  • Faster for small teams and early prototypes.
  • Less duplication between schema and implementation.

Weaknesses:

  • Easy to introduce breaking changes unintentionally.
  • Documentation quality depends on annotations.

Hybrid Approach for Advanced Teams

Many mature teams use contract-first for external APIs and code-first for internal APIs, with automated checks to detect breaking changes.

OpenAPI Contract Example

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
openapi: 3.0.3
info:
  title: Orders API
  version: 1.0.0
paths:
  /orders:
    post:
      summary: Create an order
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/OrderRequest"
      responses:
        "201":
          description: Created
components:
  schemas:
    OrderRequest:
      type: object
      properties:
        customerId:
          type: string
        items:
          type: array
          items:
            type: string

Spring Boot Integration

1
2
3
4
5
6
7
8
9
@Validated
@RestController
@RequestMapping("/orders")
public class OrderController {
    @PostMapping
    public ResponseEntity<Void> createOrder(@RequestBody OrderRequest request) {
        return ResponseEntity.status(HttpStatus.CREATED).build();
    }
}

Governance and Automation

To make contract-first work at scale:

  • Use consumer-driven contract tests to validate compatibility.
  • Enforce breaking-change detection in CI.
  • Version contracts and publish them to a registry.

Summary

Contract-first improves consistency and collaboration when many clients depend on the API. Code-first is faster for internal APIs but requires stricter review processes to avoid breaking changes. Advanced teams often adopt a hybrid model.

This post is licensed under CC BY 4.0 by the author.