API-First Design
Defining the API contract before writing implementation code, then generating servers and clients from the spec.
API-first design means you write the API specification before you write any implementation code. The spec becomes the source of truth, and you generate servers, clients, and documentation from it.
The workflow looks like this:
- Define the contract in OpenAPI (for HTTP) or Protobuf (for gRPC).
- Generate server stubs, client libraries, and types from the spec.
- Implement the server by filling in the generated interface.
The generated code gives you strong types and compile-time checks. If you break the contract, the code won't compile. This is a massive improvement over hand-written routes where a typo in a JSON field name only shows up at runtime.
The same spec can generate code for any language your stack needs. A backend team can produce a typed server while the frontend team pulls a typed HTTP client in TypeScript, and a mobile app pulls its own client in Swift or Kotlin. Everyone works against the same contract.
For internal service-to-service communication, gRPC and Protobuf take this even further. The generated code is stricter than OpenAPI: you can't accidentally return data that doesn't match the schema.
API-first pairs well with Clean Architecture. The generated HTTP types belong in the adapters layer, separate from your domain models. You convert between the two explicitly, which prevents your API contract from leaking into your business logic or database schema.
The trade-off is that you need to learn the spec format and maintain it. For teams that collaborate across frontend and backend, or across multiple services, the upfront investment pays off quickly. Everyone works against the same contract, and breaking changes are caught before they reach production.
References
- The Go libraries that never failed us: 22 libraries you need to know — Recommends oapi-codegen for generating Go servers and clients from OpenAPI specs. Explicitly advises against generating the spec from Go code, advocating a spec-first workflow where Go code is generated from the OpenAPI definition.
- Robust gRPC communication on Google Cloud Run (but not only!) — Shows how gRPC and Protobuf enforce robust contracts between services. Compares gRPC's stricter code generation with OpenAPI, where it's still possible to return invalid data despite generating servers and clients from a spec.
- Common Anti-Patterns in Go Web Applications — Recommends generating HTTP models and routes from OpenAPI definitions, database models from SQL schemas, and gRPC models from Protobuf files. Generated code provides strong types and compile-time checks instead of passing interface{} to generic functions.
- When to avoid DRY in Go — Illustrates the pitfall of sharing a single OpenAPI-generated struct for both API responses and database storage. Shows why API models and internal models should be separate, even when generated from a spec.
- Building a serverless application with Go, Google Cloud Run and Firebase — Introduces OpenAPI for generating a JavaScript HTTP client and Go HTTP server from a shared specification, keeping API contracts between frontend and backend in sync automatically.
- When using Microservices or Modular Monolith in Go can be just a detail? — Highlights the importance of keeping API contracts separate from domain types. Making the domain type serializable to JSON would couple API contract changes to domain changes and vice versa.
- 4 practical principles of high-quality database integration tests in Go — Notes that confidence in deployments increases when contracts between services are robust because of gRPC, Protobuf, or OpenAPI.
- Synchronous vs Asynchronous Architecture — Discusses how API design principles apply to both synchronous and asynchronous systems. Whether it's a REST API with an OpenAPI document or event schemas with Protobuf, the contract is the source of truth and must remain stable.