The new reservations service had no code, no database schema, and three teams already arguing. Product wanted holds that expire. The mobile team wanted one endpoint, not five. Backend wanted the error envelope settled before controllers were written. The usual outcome is a two-hour meeting, a wiki page nobody opens again, and an integration week at the end of the quarter where everyone discovers the contract meant something different to each side.

We spent that afternoon in an OpenAPI workspace instead. Not because a document prevents arguments — but because a machine-readable document turns arguments into decisions that can be tested before a single route exists.

Draft the behavior, not the routes

The AI designer in the spec workspace starts from business behavior, which is the part humans actually agree on. The first prompt was deliberately concrete:

Members reserve a time slot. A duplicate reservation for the same member and slot returns 409. A hold is created in a held state, expires after 15 minutes without payment, and then releases the slot. Confirmation emits an event the client can subscribe to. Money is integer cents. All mutating requests accept an Idempotency-Key.

The assistant returned a plan — the endpoint set, the state machine, and the schemas — before touching the document. That ordering matters. A plan is cheap to disagree with; a generated file full of endpoints feels done when it is merely large. We cut two endpoints at the plan stage and merged the hold and confirm flows into one resource.

Every AI edit is a diff you accept

Accepted proposals land as patch cards: a rendered diff against the current spec, apply or reject, with a revision log behind it. This is the same review gate as any pull request, and it produces the same behavior — the senior engineer reads the contract change instead of trusting it. Manual edits are never silently overwritten; when the AI and a human edit the same operation, the human's version is the one that survives, and the assistant works from the new state.

The decisions that usually cause integration pain are exactly the ones worth forcing into the open at this stage:

  • Money. Integer cents with an ISO 4217 currency field, never a float. The AI suggested dollars once; the diff caught it in ten seconds.
  • Idempotency. Every state-changing request takes Idempotency-Key; replaying a hold request returns the original hold instead of double-booking.
  • Errors. One envelope — error.code, error.message, optional error.details — used by every 4xx and 5xx, so clients write one handler.
  • Pagination. Cursor-based for the audit log, with the cursor opaque; no page versus pageSize argument three months later.
  • Concurrency. The slot resource carries a version; a stale update returns 409 with the current state.

The agreed hold operation ended up small and specific:

/holds:
  post:
    summary: Create a 15-minute hold on a slot
    parameters:
      - in: header
        name: Idempotency-Key
        required: true
        schema: { type: string }
    requestBody:
      required: true
      content:
        application/json:
          schema:
            type: object
            required: [slotId]
            properties:
              slotId: { type: string, format: uuid }
    responses:
      '201':
        description: Hold created; confirm within 15 minutes
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/Hold'
      '409':
        description: Slot already held or booked
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/Error'

The spec is executable on day one

This is where design-first stops being paperwork. With no backend written, the mock server answers these routes from the examples in the document, and scenario tests define what "done" means before implementation starts:

  1. Create a hold, assert 201 and capture the hold id.
  2. Repeat the identical request with the same idempotency key, assert the same hold id comes back.
  3. Attempt a second hold on the slot, assert 409 and the shared error envelope.
  4. Subscribe to the confirmation stream and assert the event arrives after confirm.

The mobile team built against the mock the same afternoon. When the backend landed, the environment switched from mock to staging and the same scenario suite ran unchanged — which is the first real evidence the implementation matches what everyone agreed to. We described the blocking problem this solves in don't block frontend work on a missing backend, and the review discipline for a larger AI-designed surface in 40 endpoints and the review gates that kept them honest.

Why the human stays in the loop

An AI model has never met your compliance officer and does not know that holds in your jurisdiction cannot exceed fifteen minutes, or that refunds have a different envelope than validation errors. It is an excellent drafter and a bad authority. The workflow that works is the boring engineering one applied to a new tool: behavior in, proposed contract out, diff reviewed like code, executable scenarios as the definition of done. The questions of who owns the contract and who defines done when AI writes the code are worth taking seriously before the first sprint.

Once the contract is agreed, the direction reverses: generate the code from the spec, and keep verifying it against the same scenarios.