OpenAPI-First vs. Code-First: Which Workflow for Which Team?
AI generated
{ }
GET
OpenAPI · API-First · Code-First · API Design · Workflow
OpenAPI-First vs. Code-First
Which Workflow for Which Team?

The choice between OpenAPI-First and Code-First is not a technical decision, it is an organizational one. It depends on team size, product maturity, number of consumers and synchronization requirements. This guide provides a structured decision framework, with concrete tooling recommendations for both approaches.

14 min read OpenAPI-First · Code-First · API Gateway · Codegen · Contract Testing Symfony · PHP · NelmioApiDoc · openapi-generator

1. What OpenAPI-First and Code-First mean

In the Code-First approach, the API is implemented in code and the OpenAPI specification is generated afterwards, often automatically, from annotations, attributes or reflection. The specification is a byproduct of the code. In the OpenAPI-First approach (also called API-First or Design-First), the specification is written first, then code is generated from it: server stubs, client SDKs and validation middleware. The specification is the source of truth, code is a byproduct of the spec.

This seemingly technical distinction has fundamental organizational consequences. Code-First means the interface is the result of implementation decisions. A framework default, a naming convention or a refactor changes the API. OpenAPI-First means the interface is an explicit decision. Changes to the API require a deliberate change to the specification, which can then be discussed and approved as a change request. Which approach is better depends entirely on context.

Hybrid approaches also exist: Code-First with strict linting rules and contract tests that ensure the generated spec meets defined standards. Or OpenAPI-First for the initial API definition, followed by Code-First for incremental extensions with automatic spec generation and drift detection. In large teams, however, the cleanest solution is almost always pure OpenAPI-First with clear governance for spec changes.

2. Code-First: Strengths, weaknesses and when it fits

The biggest advantage of Code-First is development speed in early phases. A team that needs to iterate quickly and does not yet know exactly what the API should look like can implement endpoints, try them out and change them again, without having to update a spec first every time. Documentation is generated automatically and always stays in sync with the code. No touching YAML, no manual updates after refactors.

The weaknesses become apparent as the API grows and the number of consumers increases. The generated spec reflects implementation details, not the intended API design. Fields are named like database columns or class properties, not in a way that makes sense for consumers. Breaking changes happen accidentally through refactoring. Multiple teams cannot start parallel frontend/backend development because the API has to exist before it can be consumed. For internal APIs in small, agile teams, however, Code-First is often the more pragmatic choice.


# Code-First in Symfony with NelmioApiDocBundle, attribute-based annotation

namespace App\Controller\Api;

use Nelmio\ApiDocBundle\Annotation\Model;
use OpenApi\Attributes as OA;
use Symfony\Component\Routing\Attribute\Route;
use Symfony\Component\HttpFoundation\JsonResponse;

#[OA\Tag(name: 'orders')]
#[Route('/api/v2/orders', name: 'api_')]
class OrderController extends AbstractApiController
{
    #[Route('', name: 'orders_create', methods: ['POST'])]
    #[OA\Post(
        path: '/orders',
        summary: 'Create a new order',
        requestBody: new OA\RequestBody(
            required: true,
            content: new OA\JsonContent(ref: new Model(type: CreateOrderRequest::class))
        ),
        responses: [
            new OA\Response(
                response: 201,
                description: 'Order created',
                content: new OA\JsonContent(ref: new Model(type: OrderResponse::class))
            ),
            new OA\Response(response: 400, description: 'Validation error'),
            new OA\Response(response: 401, description: 'Not authenticated'),
        ]
    )]
    public function create(CreateOrderRequest $request): JsonResponse
    {
        // Implementation...
        return $this->created($order);
    }
}

# Generate spec:
# bin/console nelmio:apidoc:dump --format=yaml > openapi.yaml
# Or directly in the browser via /api/doc.json

3. OpenAPI-First: Strengths, weaknesses and when it fits

The decisive advantage of OpenAPI-First is the decoupling of design and implementation. Frontend teams can develop against a mock server generated directly from the spec, before a single line of backend code exists. This eliminates the most common bottleneck in API development: waiting for the backend developer. Teams can work in parallel, and the API is the explicitly agreed contract document between all parties involved.

Another advantage: contract testing becomes trivial. If the spec is the source of truth, the implementation can be automatically validated against it. Tools like Dredd, Schemathesis or openapi-contract-validator generate tests from the spec and check whether the implementation fulfills them, without any manually written tests. And every change to the API is an explicit, reviewable change to the spec file, not a hidden change introduced through refactoring.

The weakness: initial effort and YAML overhead. OpenAPI-First requires that the team is able and willing to write YAML. For small teams with few consumers, this effort can be disproportionate. And if the spec and the code are not synchronized automatically, drift sets in: the spec becomes outdated, the implementation diverges, and the contract becomes worthless. OpenAPI-First without drift detection is worse than Code-First without a spec at all.

4. The decision framework: Which approach for which team?

The decision between OpenAPI-First and Code-First should be based on five factors: number of API consumers (many external consumers → OpenAPI-First), need for parallelization (frontend and backend working in parallel → OpenAPI-First), team size (small team, fast iteration → Code-First), API stability (frequent breaking changes → Code-First as long as it remains unstable), and governance requirements (formal change review process → OpenAPI-First).

A simple decision tree: Does the API have more than three external consumers? Then OpenAPI-First. Do frontend and backend need to develop in parallel? Then OpenAPI-First. Is the API still in an experimental stage with frequent breaking changes? Then Code-First, but with versioning. Is the team smaller than four backend developers with no external consumers? Then Code-First with NelmioApiDoc. In all other cases: OpenAPI-First.


# OpenAPI-First workflow, server stub generation with openapi-generator

# 1. Write the spec (openapi.yaml)
# 2. Generate server stubs
# openapi-generator generate \
#   -i openapi.yaml \
#   -g php-symfony \
#   -o src/Generated \
#   --additional-properties=invokerPackage=App\\Generated

# 3. Implement the interface (do not overwrite it!)
# Generated: src/Generated/Api/OrdersApiInterface.php
# Implement: src/Api/OrdersApiImpl.php

# openapi-generator config (openapitools.json)
{
  "$schema": "https://openapi-generator.tech/schemas/config.json",
  "generatorName": "php-symfony",
  "inputSpec": "./openapi.yaml",
  "outputDir": "./src/Generated",
  "additionalProperties": {
    "invokerPackage": "App\\Generated",
    "apiPackage": "App\\Generated\\Api",
    "modelPackage": "App\\Generated\\Model",
    "phpLegacySupport": false,
    "composerPackageName": "mironsoft/api-generated"
  },
  "globalProperties": {
    "modelTests": "false",
    "apiTests": "false",
    "modelDocs": "false",
    "apiDocs": "false"
  }
}

# 4. Contract test against a running API
# schemathesis run openapi.yaml --base-url=http://localhost:8080
# Automatically tests all endpoints against the spec

5. Tooling for Code-First in PHP/Symfony

The standard tool for Code-First in Symfony is NelmioApiDocBundle, which generates OpenAPI specs from PHP attributes (or annotations). From version 4 onward it generates OpenAPI 3.0 specs, version 5 supports OpenAPI 3.1. The bundle integrates with Symfony Form Types, JMS Serializer and Symfony Serializer for automatic schema generation from request/response classes. The generated spec can be exported as JSON via a built-in endpoint or as YAML via the console command.

For better Code-First quality, a few additional tools are worthwhile: Spectral as a linter that checks the generated spec against your own rules, openapi-diff which detects breaking changes between spec versions, and Dredd or Schemathesis for contract testing. These three tools address the biggest Code-First weaknesses: inconsistent format (Spectral), accidental breaking changes (openapi-diff) and drift between spec and implementation (contract tests).

6. Tooling for OpenAPI-First

The standard stack for OpenAPI-First consists of four components: Redocly CLI for linting and bundling the spec files, openapi-generator for client SDK and server stub generation, a mock server (Prism from Stoplight or openapi-mock) generated from the spec, and a contract test runner that validates the implementation against the spec.

In practice the OpenAPI-First development loop looks like this: a designer/architect writes or updates the spec. redocly lint checks syntax and standards. Developers open a pull request. Automatic check in CI: linting, breaking change detection with openapi-diff, and contract tests against the current staging environment. After merge: openapi-generator generates new client SDKs and publishes them to the internal package registry. The mock server is updated with the new spec. This setup makes every step automatic and reviewable.

7. Migrating from Code-First to OpenAPI-First

Teams that already develop Code-First and want to switch to OpenAPI-First cannot simply reverse the process. The generated spec is often incomplete, inconsistent or insufficiently documented to serve as a "single source of truth." The recommended migration path is incremental: first lint the generated spec with Spectral and fix the most common quality issues. Then extract the spec into a separate file and version it. Then introduce contract tests that ensure the implementation matches the spec. Only then establish the spec as the authoritative process.

The critical moment in the migration is the transition from "spec as documentation" to "spec as contract." That requires discipline: changes to the API may only start with changes to the spec, not through direct code refactoring. Breaking changes in the spec are reviewed and communicated. The CI pipeline prevents code from being deployed that does not match the spec. This cultural shift is harder than the technical migration.

8. Direct comparison of the two approaches

The table below summarizes the key differences between OpenAPI-First and Code-First and gives recommendations for different scenarios.

Criterion Code-First OpenAPI-First Recommendation
Development speed (initial) Fast Slower (spec overhead) Code-First for MVPs
Parallel development (FE+BE) Not possible Via mock server OpenAPI-First from 2+ teams
Breaking change detection Manual or through tests Automatic via openapi-diff OpenAPI-First with external APIs
Documentation quality Depends on annotations Explicit and complete OpenAPI-First for public APIs
Client SDK generation Possible but fragile Reliable from the spec OpenAPI-First for SDK publishers

9. Summary

OpenAPI-First and Code-First are not absolute opposites, but points on a spectrum between "maximum flexibility" and "maximum control." Code-First wins for fast iteration, small teams and internal API use. OpenAPI-First wins for external consumers, parallel frontend/backend development, formal change processes and SDK publishing. Moving from Code-First to OpenAPI-First is a cultural migration, not just a technical one, and should be implemented incrementally with clear milestones.

The pragmatic recommendation for most teams: start Code-First while the API is internal and still in flux. Switch to OpenAPI-First once the first version is stable and external consumers start joining. During the transition, use Spectral linting and contract tests to secure the quality of the generated spec. That gives you development speed early in the project and full control once the API becomes business critical.

OpenAPI-First vs. Code-First: The essentials at a glance

Choose Code-First when...

API in an experimental stage, small team, internal use, fast iteration matters more than stability. NelmioApiDocBundle plus Spectral as a linter.

Choose OpenAPI-First when...

External consumers, parallel FE/BE development, SDK generation, formal change governance. Redocly CLI plus openapi-generator plus mock server.

Migration

Incrementally: first improve spec quality with Spectral, then introduce contract tests, then establish the spec as the authoritative process.

Decision test

More than 3 external consumers? Then OpenAPI-First. Frontend/backend in parallel? Then OpenAPI-First. Otherwise: Code-First while agile, OpenAPI-First from v1.

Mironsoft

API strategy, OpenAPI design and development process optimization

Want to optimize your API development process?

We help teams build a structured API development process, from the OpenAPI-First vs. Code-First decision through tooling setup to CI integration with contract testing.

Process consulting

Which API workflow fits your team and your consumer structure

Tooling setup

Setting up Redocly, openapi-generator, mock server and contract tests

Migration

Migrate incrementally from Code-First to OpenAPI-First

10. FAQ: OpenAPI-First vs. Code-First

1Main difference: OpenAPI-First vs. Code-First?
Code-First: the implementation is the source of truth. OpenAPI-First: the spec is the source of truth. Determines who makes API design decisions: developers on the fly or architects deliberately.
2When is Code-First better?
Internal APIs, small teams, early project phases with frequent changes, no external consumer. Development speed matters more than formal control.
3Good documentation possible with Code-First?
Yes, with complete PHP attributes, Spectral linting and contract tests. But the extra effort often exceeds the initial effort of OpenAPI-First.
4What is spec drift?
Spec and implementation diverge. Prevent it: automatic spec generation (Code-First) or contract tests with Schemathesis (OpenAPI-First). A CI step on deviation.
5Develop frontend without a backend?
Generate a mock server from the spec: prism mock openapi.yaml. It responds to all defined endpoints with examples. The frontend can be fully developed without a running backend.
6What is contract testing?
Automatic validation of whether an API implementation matches its spec. Schemathesis generates requests from the spec and checks the responses. No manual writing needed.
7Code-First tool for Symfony?
NelmioApiDocBundle (v4 for 3.0, v5 for 3.1) plus Spectral linting plus openapi-diff for breaking changes plus Schemathesis for contract tests.
8OpenAPI-First tool stack?
Redocly CLI plus openapi-generator plus Prism mock server plus Schemathesis. Stoplight Studio as a GUI editor for non-technical stakeholders.
9How long does the migration take?
2-4 weeks technically for 20-30 endpoints. The cultural shift (spec as the authoritative process) takes another 1-2 months.
10Are hybrid approaches possible?
Yes, design new endpoints OpenAPI-First, leave existing ones Code-First and migrate them incrementally. But a unified process is more maintainable long term than a permanent mixed setup.