Skip to main content

Work design-first by example

· 4 min read

The practices that have had the biggest impact on my work as a developer have been those that help me to think, learn, and work together with others. That is why I have always liked the idea of working on our APIs design-first.

But doing design in OpenAPI has never felt quite right. When I design an API the thing I’m thinking about in my head it is almost always examples. I can talk about examples, share them with consumers for feedback and imagine myself sending, and receiving data from the API.

Examples are great thought-objects for the design phase. But when I’m done doing design, it’s great to have a schema to help during implementation. How could we get the best of both worlds?

Design-first with examples

At Optic we’ve been helping teams adopt API-first practices. Design-first is one of those ideas everyone wants to try, but everyone has trouble putting into practice.

So what if designed our APIs using examples, right in OpenAPI?

Below is a valid OpenAPI document, but it looks different from what you are used to. We have left out the schema attribute and just focused on writing the example. You could share this with your team, consumers, and even people who are not OpenAPI experts. Examples make it easy to reason about the design being considered no matter who you are:

openapi: 3.0.1
info:
version: 0.0.0
title: Cities
paths:
"/city/{code}":
parameters:
- name: code
in: path
example: PHL
schema:
type: string
required: true
get:
operationId: getCityInfo
responses:
200:
description: "Results"
content:
application/json:
example:
name: Philadelphia
code: PHL
population_size: 1500000
country_code: USA
404:
description: "Not Found"
content:
application/json:
example:
message: "City Code 'PHL' not found"
docs: "http://myapi.com/docs#getCityInfo"

You could iterate on the design by sharing and changing the examples. Then, when you’re ready to go from design → implementation run:

optic update --learn-examples

And just like that, Optic has read your examples, diffed them against the spec, and patched the specification to include a schema that matches your example 👇 

openapi: 3.0.1
info:
version: 0.0.0
title: Cities
paths:
"/city/{code}":
parameters:
- name: code
in: path
example: PHL
schema:
type: string
required: true
get:
operationId: getCityInfo
responses:
200:
description: "Results"
content:
application/json:
schema:
type: object
properties:
name:
type: string
code:
type: string
population_size:
type: integer
country_code:
type: string
required:
- name
- code
- population_size
- country_code
example:
name: Philadelphia
code: PHL
population_size: 1500000
country_code: USA
404:
description: "Not Found"
content:
application/json:
schema:
type: object
properties:
message:
type: string
docs:
type: string
required:
- message
- docs
example:
message: "City Code 'PHL' not found"
docs: "http://myapi.com/docs#getCityInfo"

Optic collaborates with you, and will never overwrite changes you make manually. You can feel safe going back to add description, format or other keywords to the schemas Optic generated.

You can run optic update --learn-examples as many times as you want, even if there’s already a schema.

Let’s add code to the example:

schema:
type: object
properties:
message:
type: string
description: A helpful error message # I wrote this manually
docs:
type: string
description: A link to the docs # I wrote this manually
required: # I manually made `docs` optional
- message
example:
message: "City Code 'PHL' not found"
docs: "http://myapi.com/docs#getCityInfo"
code: 154

Now when I run optic update --learn-examples:

code is added to the schema, but my manual changes were preserved. Optic is not "generating the spec", it’s helping me write it.

schema:
type: object
properties:
message:
type: string
description: A helpful error message # still here!
docs:
type: string
description: A link to the docs # still here!
code: # added inline
type: number
required:
- message
- code
example:
message: "City Code 'PHL' not found"
docs: "http://myapi.com/docs#getCityInfo"
code: 154

Imagine if you could design an entire API with examples, then end up with an OpenAPI on the other end? We think this can help a lot of teams start working design-first.

Is this cool? What would you like to see next? We’ve been building tools like this every week with our beta partners. If you want to help us come up with and try out crazy ideas like this, join the beta! All this is open source and publicly available soon.