Especificación
El repositorio git, la documentación de learning y los ejemplos ayudan bastante a iniciar en este viaje.
Es importante tener conocimiento de YAML y JSON para entender las especificaciones.
Vamos a partir de la definición más simple de una API.

No todos los campos son necesarios pero es buena práctica declarar al menos los campos abajo en info.
openapi: '3.0.3'
info:
title: OpenAPI Learning
version: '1.0.0'
description: 'Simple API to start learning'
paths: {}
En el repositorio, dentro de la carpeta versions, podemos ver una documentación muy rica sobre el objeto de definición de la API, es decir, la estructura que se espera del archivo yaml que vamos a crear. También podemos ver esta misma documentación en el sitio de swagger https://swagger.io/specification/. Durante el proceso de aprendizaje es necesario estar bien familiarizado con esta página.
Este es el top level de la especificación y en cada uno de estos types podemos ver cómo funciona cada uno de esos objetos.

info (Required)
Proporciona los metadatos sobre la API. Es buena práctica tener buena información para la documentación de la API. Vamos a completar lo máximo que tenemos para ver lo que podemos hacer. Dentro del Objeto de info tenemos dos objetos más (contact y license).
openapi: '3.0.3'
info:
title: OpenAPI Learning # Obligatorio
summary: First API Specs.
version: '1.0.0' # Obligatorio
description: 'Simple API to start learning'
# Términos de servicio también espera una URL
termsOfService: https://buymeacoffee.com/davidpuziol/ #:D
contact: # Objeto de contact
name: API Support Learning
url: https://devsecops.puziol.com.br/docs/category/openapi
email: [email protected]
license: # Objeto de license
name: Apache 2.0
url: https://www.apache.org/licenses/LICENSE-2.0.html
paths: {}

servers (Optional)
Es una manera de proporcionar a los usuarios finales información sobre dónde la API está disponible. Cada objeto server dentro posee url (obligatorio), descripción y variables.
openapi: '3.0.3'
### INFO
info:
title: OpenAPI Learning
summary: First API Specs.
version: '1.0.0'
description: 'Simple API to start learning'
termsOfService: https://buymeacoffee.com/davidpuziol/ #:D
contact:
name: API Support Learning
url: https://devsecops.puziol.com.br/docs/category/openapi
email: [email protected]
license:
name: Apache 2.0
url: https://www.apache.org/licenses/LICENSE-2.0.html
### SERVERS
servers:
- url: https://development.example.com/v1
description: Development server
- url: https://staging.example.com/v1
description: Staging server
- url: https://api.example.com/v1
description: Production server
paths: {}

Es posible definir variables pero es menos usual. Si fuéramos a hacer el mismo ejemplo anterior podríamos hacer así.
servers:
- url: https://api.example.com/v1
description: Production server
- url: https://{environment}.example.com/{basePath}
description: The Server for Each Environment
variables:
environment:
description: This value select api to specific environment at example.com`
default: development
enum:
- 'development'
- 'staging'
basePath:
default: v1

paths (Required)
Llegamos a la API y vamos ¿a dónde? Es necesario proporcionar algunos caminos (endpoints).
Aunque esté vacío el path necesita ser definido por lo tanto tenemos el paths: {} para satisfacer el schema del OpenAPI.
Aquí es donde todo sucede, entonces es necesario dedicar un tiempo mayor para aprender. El objeto en sí es una matriz de objetos del tipo path items.
paths:
/: # Objeto del tipo path items que posee muchas definiciones dentro
/products: # path item
# Cualquier caso que tenga product/1 product/2 caerá aquí
/product/{productId}: # path item
Observe que NO es un array de objeto, sino una matriz, si fuera array sería como el ejemplo abajo. No es array pues un array es posicional y una matriz no.
# Solo para entender la diferencia
path:
- /: # Objeto vacío asociado a la clave "/"
- /products: # Objeto vacío asociado a la clave "/products"
- /product/{productId}: # Objeto vacío asociado a la clave "/product/productID"
Ahora vamos a lo que de hecho define cada uno de los paths que es el objeto path item.
Path Items
El Path Item se resume al objeto que definirá un path. En cada path tenemos su descripción, un id de referencia, parámetros y todos los verbos que espera recibir, etc.
path:
/products: # Path Object
# Todos estos son path items objects
$ref: "" # Espera una string
summary: ""
description: ""
get: {} # Espera un Operation Object aquí dentro #<<<<<<
put: {} # Espera un Operation Object aquí dentro
post: {} # Espera un Operation Object aquí dentro
delete: {} # Espera un Operation Object aquí dentro
options: {} # Espera un Operation Object aquí dentro
head: {} # Espera un Operation Object aquí dentro
patch: {} # Espera un Operation Object aquí dentro
trace: {} # Espera un Operation Object aquí dentro
servers: {} # Espera un server object aquí dentro
parameters: {} # Espera un Parameter Reference aquí dentro
Un objeto de operación necesita de una respuesta, no vamos a entrar aún en detalles sobre eso, solo vamos a construir un path con lo mínimo necesario para una operación y más adelante profundizaremos en el objeto de operaciones.
##info:
# Removido para que quede solo lo que interesa por ahora
## ...
paths:
/v1/customers:
get:
responses: # Dentro de un objeto de operación tenemos las posibles respuestas
'200':
description: List of Customers

Podemos tener varias respuestas para una misma operación. No vamos a entrar en detalles sobre los datos aún. Las respuestas están basadas en los códigos que presentamos anteriormente.
Si observaste usamos /v1/customer ahora. Esto es una convención. Podríamos declarar /customer, pero si un día queremos tener dos versiones de este mismo path será mucho más trabajoso de cambiar, entonces procura siempre utilizar /versión/path.
¿Por qué usar /v1 es una convención común?
- Control de versiones explícito: Agregar la versión (v1, v2, etc.) al camino de la API deja claro para los consumidores qué versión están usando. Esto ayuda a evitar breaking changes (cambios que rompen compatibilidad) cuando nuevas versiones son lanzadas.
- Facilidad de transición: Cuando una nueva versión de la API es lanzada, la antigua puede continuar activa (/v1) mientras la nueva (/v2) es adoptada gradualmente por los clientes.
- Desacoplamiento de versión e implementación: El backend puede cambiar sin afectar a los consumidores de la API, siempre que mantenga la versión correspondiente en el endpoint.
- Estándar ampliamente utilizado: Muchos frameworks, herramientas y documentaciones asumen que una API tiene versión en los caminos. Esto facilita integración y familiaridad para desarrolladores.
- Separación de responsabilidad: Mantener versiones explícitas permite que diferentes versiones sean gestionadas por equipos distintos o incluso en infraestructuras separadas.
- Es útil si la API va a evolucionar rápidamente.
Existen otras técnicas para resolver este caso, pero ¿hace mal? ¿No? ¡entonces mantén!