An API doesn't just get created and forgotten. It goes through a full lifecycle:
Design → Document → Build → Test → Publish → Version → Deprecate
Each stage has its own tools, practices, and failure modes. This guide walks through all of them.
1. API Design Principles
Before writing a single line of code, define the API contract. A well-designed API is stable, predictable, and easy to consume.
RESTful Design Checklist
Resources (nouns, not verbs):
✅ GET /api/v1/orders/{id}
❌ GET /api/v1/getOrder?id=123
HTTP methods map to actions:
┌────────────┬──────────────────────────────┐
│ GET │ Read resource │
│ POST │ Create resource │
│ PUT │ Replace resource (full) │
│ PATCH │ Update resource (partial) │
│ DELETE │ Delete resource │
└────────────┴──────────────────────────────┘
HTTP status codes carry meaning:
┌────────────┬──────────────────────────────┐
│ 200 │ OK │
│ 201 │ Created │
│ 400 │ Bad Request (client error) │
│ 401 │ Unauthorized │
│ 403 │ Forbidden │
│ 404 │ Not Found │
│ 429 │ Too Many Requests │
│ 500 │ Internal Server Error │
└────────────┴──────────────────────────────┘
Design-First vs Code-First
| Approach | When to Use | Tool |
|---|---|---|
| Design-First | New projects; API is a product | Swagger Editor / Stoplight |
| Code-First | Existing projects; API evolves with code | Swagger annotations / SpringDoc |
Design-first forces teams to agree on the contract before implementation begins — reducing integration surprises downstream.
2. API Creation
Design-First: Swagger Editor
Write the API spec in YAML/JSON using OpenAPI format, with live preview:
openapi: 3.0.0
info:
title: Order Service API
version: 1.0.0
paths:
/orders/{id}:
get:
summary: Get order by ID
parameters:
- name: id
in: path
required: true
schema:
type: string
responses:
'200':
description: Order found
'404':
description: Order not found
Swagger Editor provides:
- Real-time spec validation
- Live documentation preview
- Code generation (client SDKs + server stubs)
Code-First: Scan from Annotations
Used primarily during project iteration — add Swagger dependencies and annotations to auto-generate docs from code:
@RestController
@RequestMapping("/api/v1/orders")
@Tag(name = "Order API", description = "Order management endpoints")
public class OrderController {
@GetMapping("/{id}")
@Operation(summary = "Get order by ID")
@ApiResponse(responseCode = "200", description = "Order found")
public ResponseEntity<Order> getOrder(@PathVariable String id) {
// ...
}
}
The spec is auto-generated at build time — always in sync with the actual code.
3. API Testing
API testing covers three distinct concerns:
┌──────────────────────────────────────────────────────────────┐
│ 1. Functional Testing — does the API behave correctly? │
│ 2. Contract Testing — does the API match its spec? │
│ 3. Performance Testing — can the API handle the load? │
└──────────────────────────────────────────────────────────────┘
3.1 Functional Testing
Test all parameter combinations — both valid and invalid inputs:
For each endpoint, test:
├── Happy path: valid inputs → expected response
├── Edge cases: boundary values, empty arrays, null fields
├── Error cases: missing required params → 400
├── Auth cases: no token → 401, wrong role → 403
└── Not found: non-existent resource → 404
Tools: Postman / SoapUI / REST-assured / pytest + httpx
3.2 Contract Testing
Verify that the API implementation matches its published specification:
API Spec (OpenAPI YAML)
│
▼
Contract Test Runner
├── Does every documented endpoint exist?
├── Do request/response schemas match?
├── Are required fields always present?
└── Are status codes correct?
│
▼
Pass ✅ → spec and implementation are in sync
Fail ❌ → spec is outdated OR implementation has a bug
Detecting spec drift between commits:
Use Microsoft's open source tool openapi-diff to detect API changes on every commit:
# Compare current spec against previous version
openapi-diff old-spec.yaml new-spec.yaml
# Output example:
# BREAKING: DELETE /orders/{id} — endpoint removed
# NON-BREAKING: GET /orders — new optional query param added
Integrate into CI — fail the build on any breaking change to the API contract.
Consumer-Driven Contract Testing (advanced):
Use Pact to let API consumers define their expectations, then verify the provider satisfies them:
Consumer (frontend / other service)
│ defines expected interactions
▼
Pact contract file
│
▼
Provider (API service) verifies contract in CI
│
▼
Both sides stay in sync without end-to-end tests
3.3 Performance Testing
Most application performance bottlenecks trace back to API performance. Test it before it becomes a production incident.
Step 1: Define performance requirements
| Metric | Example Target |
|---|---|
| Average throughput | 500 req/s per endpoint |
| Peak throughput | 2,000 req/s for 5 minutes |
| Concurrent users | 1,000 simultaneous connections |
| P99 response time | < 500ms under normal load |
| Error rate under load | < 0.1% |
Step 2: Choose the right test type
┌──────────────────────────────────────────────────────────────┐
│ Baseline Test │
│ Normal expected load → measure avg and peak response time │
│ Purpose: establish performance baseline for comparison │
├──────────────────────────────────────────────────────────────┤
│ Load Test │
│ Gradually increase load → observe throughput & latency │
│ Purpose: verify system handles expected peak load │
├──────────────────────────────────────────────────────────────┤
│ Stress Test │
│ Push beyond limits → find the breaking point │
│ Purpose: determine maximum throughput before degradation │
├──────────────────────────────────────────────────────────────┤
│ Soak Test │
│ Sustained load over hours/days → detect memory leaks, │
│ connection pool exhaustion, gradual degradation │
│ Purpose: validate long-term stability │
└──────────────────────────────────────────────────────────────┘
Step 3: Select tooling
| Tool | Notes |
|---|---|
| JMeter | Most widely used; GUI + scriptable; open source |
| k6 | Developer-friendly; JavaScript scripts; CI-native |
| Gatling | Scala-based; excellent HTML reports |
| LoadUI | GUI-driven; good for quick tests |
| Locust | Python-based; easy to write complex scenarios |
Performance optimization path:
Test results below target
│
▼
Tune API platform parameters
(connection pool, thread pool, timeout settings)
│
▼
Still below target?
│
▼
Profile application code
(slow queries, N+1 problems, missing indexes)
│
▼
Still below target?
│
▼
Scale hardware / infrastructure
4. API Publishing via API Gateway
Once tested, APIs are published through an API Gateway — the single entry point for all consumers.
Clients (web / mobile / third-party)
│
▼
┌─────────────────────────────────────────────────────────────┐
│ API Gateway │
│ ├── Authentication & Authorization (JWT / OAuth2 / API Key)│
│ ├── Rate Limiting & Throttling │
│ ├── Request Routing (to correct microservice) │
│ ├── Request/Response Transformation │
│ ├── SSL Termination │
│ └── Logging & Analytics │
└──────────────────────────────────────────────────────────────┘
│
▼
Backend Microservices
API Gateway options:
| Tool | Type | Notes |
|---|---|---|
| Kong | Open source | Plugin ecosystem; high performance |
| AWS API Gateway | Cloud | Serverless-native; tight AWS integration |
| Nginx | Open source | Lightweight; manual config |
| Apigee | Commercial | Enterprise-grade; Google Cloud |
| Traefik | Open source | Kubernetes-native; auto-discovery |
Publishing checklist:
Before publishing an API:
☐ All functional tests passing
☐ Contract tests passing (spec matches implementation)
☐ Performance targets met
☐ Authentication configured on gateway
☐ Rate limits defined
☐ API documentation published and versioned
☐ Changelog entry written
5. API Version Management
APIs evolve. Versioning ensures existing consumers aren't broken when the API changes.
Three Versioning Strategies
① URL Path Versioning (most common)
https://api.example.com/v1/orders
https://api.example.com/v2/orders
✅ Explicit, easy to understand, easy to route at gateway level
❌ URL changes when version bumps
② HTTP Header Versioning
GET /orders
X-API-Version: 2
✅ Clean URLs
❌ Less visible; harder to test in browser
③ Query Parameter Versioning
https://api.example.com/orders?version=2
✅ Easy to add without changing URL structure
❌ Query params are typically for filtering, not routing — semantically awkward
What Constitutes a Breaking Change?
Breaking changes (require version bump):
├── Removing an endpoint
├── Removing a required request field
├── Changing a field type (string → integer)
├── Changing HTTP method of an endpoint
└── Changing authentication scheme
Non-breaking changes (no version bump needed):
├── Adding a new optional request field
├── Adding a new endpoint
├── Adding a new optional response field
└── Relaxing validation rules
Version Lifecycle
v1 released
│
v2 released (v1 enters maintenance mode)
│
Deprecation notice sent to v1 consumers
(minimum 6-month notice period recommended)
│
v1 sunset date announced
│
v1 decommissioned
6. API Deprecation & Sunset
Deprecation is part of the lifecycle — handle it gracefully to avoid breaking consumers.
Deprecation Communication
Step 1: Add deprecation headers to v1 responses
──────────────────────────────────────────────
HTTP/1.1 200 OK
Deprecation: true
Sunset: Sat, 01 Jan 2026 00:00:00 GMT
Link: <https://api.example.com/v2/orders>; rel="successor-version"
Step 2: Update API documentation
──────────────────────────────────────────────
Mark v1 endpoints as [DEPRECATED] in Swagger UI
Link to v2 migration guide
Step 3: Notify consumers directly
──────────────────────────────────────────────
Email / developer portal announcement
Include: sunset date, migration guide, breaking change list
Step 4: Monitor v1 usage
──────────────────────────────────────────────
Track v1 traffic via API Gateway analytics
Follow up with teams still on v1 before sunset date
Step 5: Decommission
──────────────────────────────────────────────
Return 410 Gone (not 404) for sunset endpoints
Keep documentation available for reference
Full API Lifecycle Summary
┌──────────────────────────────────────────────────────────────┐
│ 1. Design OpenAPI spec (design-first or code-first) │
│ │ │
│ 2. Build Implement + auto-generate docs from code │
│ │ │
│ 3. Test Functional → Contract → Performance │
│ │ │
│ 4. Publish Deploy via API Gateway │
│ │ Auth + rate limiting + routing configured │
│ │ │
│ 5. Version URL / Header / Query param strategy │
│ │ openapi-diff in CI to catch breaking changes │
│ │ │
│ 6. Deprecate Sunset headers + consumer notification │
│ Monitor usage → decommission gracefully │
└──────────────────────────────────────────────────────────────┘
Top comments (0)