Prologue: The Slack Bomb
When I first built a backend API solo, I worked 3 weeks straight. Proud of my work, I sent a Slack message to the frontend developer:
"API is done! Test server:
https://api.example.com"
30 minutes later:
"How do I use this? What are the endpoints? Parameters?"
Panicked, I hastily created an Excel file.
| Endpoint | Method | Parameters | Description |
|---|---|---|---|
/users | GET | - | User list |
/users/{id} | GET | id (int) | User detail |
I uploaded it to Slack. "Check this and use it!"
2 weeks later:
"The doc says
userIdbut I'm getting auser_iderror?"
I had changed the API spec a week ago but forgot to update the Excel doc. The frontend dev spent 2 hours debugging "Why doesn't this work?"
That day, I learned: "Excel docs are liars."
Why Docs Always Get Stale
A backend developer's daily routine:
- Develop API (write code)
- Write docs (Excel / Notion / Confluence)
- Deploy
- (A week later) Modify code
- Deploy
- Update docs... Forgot ❌
Why do we forget? Because docs and code are separated.
- Code:
UserController.java - Docs:
API_Spec_v2.xlsx(different folder)
When you modify code, the doc file is invisible. You even forget where the doc is.
Bigger Problem: Version Control Hell
My worst experience:
API_Spec_Final.xlsx
API_Spec_Final_RealFinal.xlsx
API_Spec_Final_RealFinal_ThisOne.xlsx
API_Spec_June.xlsx
5 files like this in the folder. No one knows which is actually latest.
Realization: "Code IS the Documentation"
To solve this, docs and code must live together. That's the Swagger (OpenAPI) approach.
Old Way (Separated)
// UserController.java (Code)
@GetMapping("/users/{id}")
public User getUser(@PathVariable String id) {
return userService.findById(id);
}
// API_Spec.xlsx (Separate file)
Endpoint: /users/{id}
Method: GET
Parameter: id (String)
Problem: Code change → Forget to update doc → Lying doc.
Swagger Way (Integrated)
// UserController.java (Code + Doc)
@Operation(summary = "Get User", description = "Fetch user info by ID")
@ApiResponse(responseCode = "200", description = "Success")
@ApiResponse(responseCode = "404", description = "User not found")
@GetMapping("/users/{id}")
public User getUser(@PathVariable String id) {
return userService.findById(id);
}
The annotation (@Operation) IS the documentation.
When code changes, docs change automatically.
Swagger reads these annotations and auto-generates a beautiful website:
https://api.example.com/swagger-ui
What Swagger Generates
Visit Swagger UI and you'll see:
1. API List (Auto-generated)
GET /users/{id} - Get User
POST /users - Create User
DELETE /users/{id} - Delete User
It finds @GetMapping, @PostMapping in code and auto-creates the list.
2. Parameter Descriptions
Path Parameters:
- id (String, required): User ID
Response:
{
"id": "user123",
"name": "John Doe",
"email": "john@example.com"
}
Reads User class fields and auto-shows sample response.
3. "Try it out" Button
This is the killer feature. You can execute APIs directly in the browser.
- Enter "user123" in
idfield - Click "Execute"
- See actual response:
{
"id": "user123",
"name": "John Doe",
"email": "john@example.com"
}
No Postman needed.
My Real Experience Adopting Swagger
When I added Swagger to my blog backend (Next.js API Routes):
Before (No Swagger)
Frontend dev (my colleague):
"What's the body format for POST /posts?"
Me (Backend):
"Wait... (digging through code) Send
title,content,tagsfields."
30 minutes later:
"Is tags a string or array?"
Me:
"Oh... array. Like
["tag1", "tag2"]."
This conversation happened 5 times a day.
After (Swagger Added)
/**
* @swagger
* /api/posts:
* post:
* summary: Create blog post
* requestBody:
* content:
* application/json:
* schema:
* type: object
* properties:
* title:
* type: string
* content:
* type: string
* tags:
* type: array
* items:
* type: string
*/
export default function handler(req, res) { ... }
Frontend dev:
"Checked Swagger docs. Worked immediately."
0 questions.
Before vs After Swagger
| Item | Without Swagger | With Swagger |
|---|---|---|
| Doc Writing Time | 1 hour (Excel) | 10 min (annotations) |
| Doc Updates | Manual (often forgot) | Auto (synced with code) |
| Testing Method | Open Postman | Browser → Execute |
| Frontend-Backend Chat | Slack question spam | Read docs → Done |
| New Dev Onboarding | 1 week (explain APIs) | 1 day (Swagger link) |
Production Code: Spring Boot + Swagger
Actual config I used at work.
1. Add Dependency (build.gradle)
implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.0.2'
2. Annotate Controller
@RestController
@RequestMapping("/api/users")
@Tag(name = "User API", description = "User management API")
public class UserController {
@Operation(
summary = "Get user list",
description = "Returns paginated list of all users"
)
@ApiResponses({
@ApiResponse(responseCode = "200", description = "Success"),
@ApiResponse(responseCode = "500", description = "Server error")
})
@GetMapping
public List<User> getUsers(
@Parameter(description = "Page number (starts at 0)")
@RequestParam(defaultValue = "0") int page
) {
return userService.findAll(page);
}
}
3. Run and Visit
http://localhost:8080/swagger-ui/index.html
Done. Documentation auto-generated.
Pro Tips for Swagger
Tip 1: Add Example Values to DTOs
@Schema(description = "User creation request")
public class CreateUserRequest {
@Schema(description = "User name", example = "John Doe")
private String name;
@Schema(description = "Email", example = "john@example.com")
private String email;
}
When you click "Try it out" in Swagger UI, example values auto-fill.
Tip 2: Configure Auth Headers
@Configuration
public class SwaggerConfig {
@Bean
public OpenAPI customOpenAPI() {
return new OpenAPI()
.addSecurityItem(new SecurityRequirement().addList("bearerAuth"))
.components(new Components()
.addSecuritySchemes("bearerAuth",
new SecurityScheme()
.type(SecurityScheme.Type.HTTP)
.scheme("bearer")
.bearerFormat("JWT")
)
);
}
}
Now an "Authorize" button appears at the top of Swagger UI. Enter JWT token once, and it auto-attaches to all API tests.
Tip 3: Disable Swagger in Production
# application-prod.yml
springdoc:
swagger-ui:
enabled: false
Use when you don't want to expose API specs publicly for security.
Swagger Limitations and Alternatives
Limitation 1: Annotation Hell
Complex APIs lead to annotations longer than code.
@Operation(...)
@ApiResponse(...)
@ApiResponse(...)
@Parameter(...)
@Parameter(...)
@GetMapping("/users")
public List<User> getUsers(...) {
// Actual logic: 3 lines
}
Solution: Write separate OpenAPI YAML file (Contract-First instead of Code-First).
Limitation 2: Runtime-Only Docs
Swagger requires running the server to see docs. Can't check at compile time.
Solution: Use Postman Collection / Insomnia (manageable as files).
Deep Dive: Design First vs Code First
There is a huge debate in the API world. "Should I write the YAML first? Or the Code first?"
1. Code First (What I did above)
- Process: Write Java/Python code -> Generate Swagger via Annotations.
- Pros:
- Fast for single developers.
- Source of Truth is the Code.
- Cons:
- Pollution of code with annotations.
- Frontend has to wait until Backend writes the controller.
2. Design First (API First)
- Process: Write
openapi.yamlmanually -> Generate Code interfaces from YAML. - Pros:
- Frontend and Backend can start working simultaneously based on the YAML contract.
- Cleaner code (no annotations).
- Better API design (you think about the interface before implementation details).
- Cons:
- Learning curve of YAML syntax.
- Syncing implementation with YAML requires discipline (or tools).
Recommendation: Start with Code First for small projects. Move to Design First when your team grows to 5+ people.
The Gold Standard: What makes "Great" Documentation?
Why do people love Stripe or Twilio documentation? It's not just because they use Swagger. It's because they have:
- Copy-Pasteable Code Snippets: In 5 languages (Curl, Python, Node, Ruby, Go).
- Clear Authentication Guide: "Where do I get my API Key?" is usually the hardest step.
- Error Dictionary: Don't just generic 400. Explain "Error 40021: Card declined due to suspicion."
- Use Cases: Tutorials like "How to charge a subscription" rather than just a list of endpoints.
Swagger handles the "Reference" part. But you still need to write the "Guides".
Useful Tools in the Ecosystem
Swagger UI is ugly. Use these tools to make it pretty:
- Redoc: Converts OpenAPI into a beautiful 3-column documentation (like Stripe).
- OpenAPI Generator: Generates SDKs (Client libraries) for Frontend/Mobile automatically.
- Stoplight: A GUI editor for editing OpenAPI YAML files (so you don't make syntax errors).
Automating Documentation with GitHub Actions
Don't manually generate Swagger JSON. Automate it.
Here is a sample GitHub Actions workflow to auto-publish your API docs to GitHub Pages or S3 whenever you push to main.
name: Publish API Docs
on:
push:
branches: [main]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Generate OpenAPI JSON
run: ./gradlew generateOpenApiDocs # Or equivalent command
- name: Upload to S3
uses: jakejarvis/s3-sync-action@master
with:
args: --acl public-read --follow-symlinks --delete
env:
AWS_S3_BUCKET: my-api-docs-bucket
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
Now, your documentation site is always up to date with your main branch code.
The Ultimate Documentation Checklist
Before you say "Docs are done", check these 5 items:
- Authentication: Is it clear how to get a token? (Bearer, API Key, OAuth)
- Base URL: Is
https://api.production.comclearly visible? - Error Codes: Do you list all possible 4xx/5xx errors?
- Rate Limits: Tell users "100 requests per minute" so they don't get blocked.
- Try It Out: Does the "Execute" button actually work? (CORS configured?)
If you have these 5, you are already in the top 10% of API documentations.
Final Thought: "Stop Using Excel"
When I first created API docs in Excel, I thought "This is the best way." But when docs and code are separated, docs always lie.
After adopting Swagger:
- Never forgot to update docs.
- Frontend-backend Q&A reduced 90%.
- New dev onboarding time: 1 week → 1 day.
Still writing API specs in Excel/Word? Adopt Swagger today. Peace will come.
"A well-documented API is the best marketing for your developer tool."