Need help with your APIs? I offer API discovery, governance & evangelism services. Explore services →
API Evangelist API Evangelist
Discovery
Learnings
Guidance
Toolbox
Alignment
API Evangelist LLC

OpenAPI Overlays for Improving Developer Experience: Enriching a Thin, Code-Generated Spec

July 3rd, 2026 · Kin Lane
OpenAPI Overlays for Improving Developer Experience: Enriching a Thin, Code-Generated Spec

You know the spec I am talking about. It was generated from code by a framework annotation scanner, and it is technically correct in every dimension and useful to nobody. The GET /products operation has a summary that reads “Get products” and a description that is blank. The Product schema fields are named perfectly and documented not at all. There are zero examples, so the docs render an empty grey box where a developer wants to see what a real product actually looks like. The date fields in the handful of examples that do exist are frozen to whatever day the original developer ran the generator, so by the time anyone reads them they are two years stale and everything looks abandoned.

The tempting fix is to go edit the generated source. Do not do that. The next time codegen runs it overwrites your careful prose, and you are back to “Get products.” This is exactly the seam that OpenAPI Overlays were built for, and it is the use case I keep pointing people at from my roundup of overlay use cases. You leave the generated spec generated, and you layer the human stuff on top as a separate, versioned artifact. Here is what that looks like against my Products API teaching template.

overlay: 1.1.0
info:
  title: Products API — Developer Experience Enrichment
  version: 1.0.0
extends: https://raw.githubusercontent.com/api-evangelist/products-api/main/openapi/products-api-openapi.yml
actions:
  - target: $.paths['/products'].get
    update:
      summary: List products
      description: >
        Returns a paginated list of products in the catalog. Results are
        sorted by most recently updated. Use the `limit` and `offset` query
        parameters to page through the collection, and watch the `RateLimit`
        response headers to stay under your quota. Each item is a full
        `Product` object — there is no thin "summary" representation, so you
        can render a listing without a second round trip.
  - target: $.components.schemas.Product.properties.name
    update:
      description: Human-readable product name shown in the storefront. 1–120 characters.
      example: Stainless Steel Water Bottle
  - target: $.paths['/products'].get.responses['200'].content['application/json']
    update:
      example:
        - id: prod_8f2c1a
          name: Stainless Steel Water Bottle
          price: 24.99
          status: active

That overlay does three things without touching the source file. The first action rewrites the useless GET /products summary and description into something a human wrote for a human, explaining pagination and pointing at the rate-limit headers. The second reaches into components.schemas.Product and gives the name property an actual description and an example value, so the rendered schema table stops looking like a stub. The third hangs a realistic response example off the 200, so the docs show a developer what a real payload looks like instead of an empty box. The extends key binds all of this to the canonical Products API spec, and the JSONPath in each target is what surgically lands the update in the right place.

Now the stale-dates problem, which deserves its own overlay so you can run it on a schedule.

overlay: 1.1.0
info:
  title: Products API — Freshen Example Dates
  version: 1.0.0
extends: https://raw.githubusercontent.com/api-evangelist/products-api/main/openapi/products-api-openapi.yml
actions:
  - target: $.components.schemas.Product.properties.created_at
    update:
      example: '2026-07-03T09:00:00Z'
  - target: $.components.schemas.Product.properties.updated_at
    update:
      example: '2026-07-03T09:14:00Z'
  - target: $.components.responses.TooManyRequests.content['application/problem+json'].example.retry_after
    update:
      example: '2026-07-03T09:15:30Z'

This one exists to be regenerated. A tiny script stamps today’s date into the example fields for created_at, updated_at, and even the retry_after in the TooManyRequests problem-details response, then reapplies the overlay in CI. The docs never show a 2024 timestamp again, and because it is a separate overlay from the prose enrichment, the date-bumping churn never touches your hand-written descriptions in git.

Applying these is boring in the good way — openapi-overlays from npm, Speakeasy, and Redocly all take an overlay and a base spec and hand you back the merged result, and you wire that into the build that publishes your docs. The mechanics are all in the Overlay 1.1.0 specification.

Here is my take. Code-first spec generation is not the enemy, but the specs it produces are a skeleton, not a product. The developer experience does not live in the annotations; it lives in the prose, the examples, and the freshness — and every one of those is a thing your source-of-truth codegen will happily obliterate on the next run. Overlays are how you keep the machine’s structure and the human’s voice in the same pipeline without either one stomping the other. Stop editing generated files. Layer it.