Verifier Dim B (homogeneity + code↔docs confrontation) flagged 5 ADRs stuck at "Partially Implemented" while the corresponding code is live. Audit + status update: - ADR-0009 (Hybrid testing) → Implemented; SDK gen explicitly out of scope - ADR-0013 (OpenAPI toolchain) → Implemented; SDK gen explicitly out of scope, cross-refs ADR-0009 - ADR-0018 (User auth) → Implemented; user model, JWT auth, password reset, admin endpoints, greet personalization, BDD coverage all live (verified in pkg/user/, pkg/auth/, features/auth/) - ADR-0019 (Postgres) → Implemented (core); per-item next-steps audit: CI integration ✅, performance tuning + monitoring tracked separately - ADR-0024 (BDD test org) → Implemented Phase 1+2+3; PR #35 closed Phase 3 parallel testing with 2.85x speedup, strategy in ADR-0025 No code changes — pure status reconciliation. The Status field is now the single source of truth for what's done vs deferred, removing the "forever Partial" doc drift the verifier flagged.
32 KiB
13. OpenAPI/Swagger Toolchain Selection
Date: 2026-04-05 Status: Implemented (OpenAPI documentation operational; SDK generation explicitly out of scope, see ADR-0009) Authors: Arcodange Team Implementation Date: 2026-04-05 Last Updated: 2026-05-05
Context
The dance-lessons-coach project requires comprehensive API documentation and testing capabilities. As the API evolves with v1 and v2 endpoints, we need a robust OpenAPI/Swagger toolchain to:
- Document APIs: Generate interactive API documentation
- Test APIs: Enable automated API testing
- Validate APIs: Ensure API contracts are met
- Generate Clients: Potentially generate API clients
- Version APIs: Support multiple API versions
Decision Drivers
- Go Integration: Must work well with Go/Chi router
- Ease of Use: Simple to implement and maintain
- Comprehensive: Support for all API endpoints and versions
- Testing: Enable both manual and automated testing
- Standards Compliance: Follow OpenAPI standards
- Documentation Quality: Generate professional, interactive docs
- CI/CD Friendly: Work well in automated pipelines
Expected Outcomes (Critical Addition)
What We Actually Need from OpenAPI Integration
1. Documentation ✅ (PRIMARY REQUIREMENT)
- Interactive API Documentation: Swagger UI for exploring endpoints
- Human-Readable Spec: Clear documentation for developers
- Endpoint Discovery: Easy to find and understand all API methods
- Request/Response Examples: Sample payloads and responses
- Version Documentation: Clear v1 vs v2 documentation
2. Tool Integration ✅ (SECONDARY REQUIREMENT)
- Web Client Testing: Postman/Insomnia import capability
- API Exploration: Interactive testing via Swagger UI
- curl Examples: Generated curl commands for endpoints
- Browser Testing: Direct API testing from documentation
- Debugging: Better visibility into API behavior
3. SDK Generation ❌ (NICE-TO-HAVE, NOT CRITICAL)
- TypeScript Client: For potential web frontend
- Go Client: For internal service communication
- Python Client: For data science/ML integration
- Mobile Clients: Future iOS/Android SDKs
- Consistency: Generated clients match API exactly
Priority Matrix
| Requirement | Priority | Current Solution | Alternative Solution |
|---|---|---|---|
| Interactive Docs | 🔴 Critical | swaggo Swagger UI | oapi-codegen Swagger UI |
| API Exploration | 🟡 Important | swaggo + Postman | oapi-codegen + tools |
| Request Examples | 🟡 Important | swaggo annotations | OpenAPI spec examples |
| Web Testing | 🟢 Nice-to-have | swaggo try-it-out | oapi-codegen try-it-out |
| SDK Generation | 🔵 Future | Manual clients | oapi-codegen clients |
| Type Safety | 🔵 Future | Manual types | Generated types |
| Validation | 🔵 Future | Manual validation | Auto validation |
Minimum Viable Integration
What we can achieve with swaggo:
- ✅ Interactive Swagger UI at
/docs - ✅ Complete API documentation
- ✅ Try-it-out functionality
- ✅ Postman/Insomnia integration
- ✅ curl command generation
- ✅ Multi-version support
- ✅ Annotation-based simplicity
What we defer to future:
- ❌ Automatic SDK generation
- ❌ OpenAPI 3.0 advanced features
- ❌ Automatic request validation
- ❌ Type-safe client generation
- ❌ Webhook/callback support
Tool Selection Based on Requirements
swaggo/swag meets 100% of current critical needs:
- ✅ Documentation: Excellent Swagger UI
- ✅ Tool Integration: Postman/Insomnia/curl support
- ✅ Ease of Use: Simple annotations
- ✅ Chi Integration: Native support
- ✅ Implementation Speed: Can deploy in days
oapi-codegen would provide additional nice-to-haves:
- ❌ SDK Generation: Not currently needed
- ❌ Type Safety: Manual types sufficient at current scale
- ❌ OpenAPI 3.0: 2.0 sufficient for documentation
- ❌ Validation: Manual validation working well
Decision Validation
Does swaggo meet our actual requirements?
- ✅ Documentation: Yes, excellent Swagger UI
- ✅ Tool Integration: Yes, Postman/Insomnia/curl
- ✅ SDK Generation: Not critical currently
- ✅ Implementation: Simple and fast
- ✅ Maintenance: Low overhead
Would oapi-codegen provide significant value?
- ❌ No immediate business need for SDK generation
- ❌ No requirement for OpenAPI 3.0 features
- ❌ Validation already implemented
- ❌ Type safety not critical at current scale
Conclusion: swaggo provides everything we actually need today.
Considered Options
Option 1: swaggo/swag + swaggo/gin-swagger
Overview: Popular Go OpenAPI/Swagger solution with Chi router support
Pros:
- ✅ Excellent Go integration
- ✅ Chi router support via
swaggo/filesandswaggo/gin-swagger(adaptable) - ✅ Simple annotation-based approach
- ✅ Generates interactive Swagger UI
- ✅ OpenAPI 2.0 and 3.0 support
- ✅ Widely used in Go community
- ✅ Good documentation
- ✅ Chi Framework Native: Designed to work with Go HTTP frameworks
Cons:
- ❌ Primarily designed for Gin (requires adaptation for Chi)
- ❌ Annotation parsing can be fragile
- ❌ Limited advanced features
- ❌ OpenAPI 2.0 primarily (3.0 support limited)
Option 1.5: Framework Compatibility Analysis (MISSING FROM ORIGINAL ADR)
Critical Oversight: The original ADR failed to properly evaluate framework compatibility, specifically:
- Echo Framework Dependency: oapi-codegen's middleware is designed for Echo framework
- Chi Integration: No native Chi support in oapi-codegen v1
- Adapter Complexity: Significant development effort needed to adapt Echo middleware to Chi
- Maintenance Burden: Custom adapters require ongoing maintenance
Framework Compatibility Matrix:
| Tool | Echo | Gin | Chi | Standard Library |
|---|---|---|---|---|
| swaggo/swag | ✅ | ✅ | ✅ | ✅ |
| go-swagger | ✅ | ✅ | ✅ | ✅ |
| oapi-codegen v1 | ✅ | ❌ | ❌ | ❌ |
| oapi-codegen v2 | ✅ | ✅ | ? | ✅ |
Lesson Learned: Framework compatibility must be a primary evaluation criterion for middleware tools.
Implementation:
import (
"github.com/go-chi/chi/v5"
"github.com/swaggo/swag"
"github.com/swaggo/files"
"github.com/swaggo/gin-swagger"
// Chi adapter would be needed
)
// @title dance-lessons-coach API
// @version 1.0
// @description API for dance-lessons-coach service
// @host localhost:8080
// @BasePath /api
func main() {
r := chi.NewRouter()
// Chi adaptation of swagger routes
r.Mount("/swagger", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
ginSwagger.WrapHandler(swaggerFiles.Handler)(w, r)
}))
}
Option 2: go-swagger/go-swagger
Overview: Comprehensive OpenAPI 2.0 toolkit for Go
Pros:
- ✅ Full OpenAPI 2.0 support
- ✅ Code generation (server + client)
- ✅ Validation capabilities
- ✅ Swagger UI integration
- ✅ Mature and stable
- ✅ Good for API-first design
Cons:
- ❌ OpenAPI 2.0 only (not 3.x)
- ❌ Steeper learning curve
- ❌ More complex setup
- ❌ Less Chi-specific integration
- ❌ Slower development cycle
Implementation:
# Generate server stubs
swagger generate server -f swagger.yml
# Generate client
swagger generate client -f swagger.yml
Option 3: oapi-codegen + Chi Middleware
Overview: Modern OpenAPI 3.0 toolchain with Chi integration
Pros:
- ✅ OpenAPI 3.0+ support
- ✅ Excellent Chi router integration
- ✅ Type-safe request/response handling
- ✅ Validation middleware
- ✅ Client generation
- ✅ Active development
- ✅ Good performance
- ✅ Strong typing
Cons:
- ❌ Newer project (less battle-tested)
- ❌ Requires separate spec file management
- ❌ More boilerplate for simple cases
Implementation:
import (
"github.com/deepmap/oapi-codegen/pkg/middleware"
"github.com/go-chi/chi/v5"
)
func main() {
r := chi.NewRouter()
// Load OpenAPI spec
swagger, err := api.GetSwagger()
if err != nil {
panic(err)
}
// Add validation middleware
r.Use(middleware.OapiRequestValidator(swagger))
// Add Swagger UI
r.Mount("/docs", middleware.SwaggerUI(middleware.SwaggerUIOpts{
Spec: swagger,
Path: "/docs/openapi.json",
}))
}
Option 4: bufbuild/connect + bufbuild/connect-go
Overview: Modern gRPC+JSON transcoding with OpenAPI support
Pros:
- ✅ gRPC + REST unified approach
- ✅ Protocol Buffers based
- ✅ OpenAPI generation
- ✅ Excellent performance
- ✅ Type safety
- ✅ Future-proof
Cons:
- ❌ Significant architectural change
- ❌ Steep learning curve
- ❌ Overkill for simple REST APIs
- ❌ Requires Protobuf expertise
- ❌ Less Swagger UI focus
Implementation:
// Would require complete API redesign to gRPC
Decision Outcome (Final Implementation)
Chosen option: swaggo/swag with embedded documentation
Rationale
After thorough evaluation and implementation, we've successfully integrated swaggo/swag with the following key advantages:
Key Reasons for Choosing swaggo/swag:
- Native Chi Integration: Works seamlessly with Chi router without adapters
- Annotation-Based: Simple, intuitive annotation syntax directly in handler code
- Mature Ecosystem: Widely adopted in Go community with excellent documentation
- OpenAPI 2.0 Support: Sufficient for current API documentation needs
- Embedded Documentation: Perfect for single-binary deployment using Go's
//go:embed - Interactive UI: Built-in Swagger UI for API exploration and testing
- Code Generation: Easy regeneration with
go generateworkflow
Implementation Reality vs Original Plan
Original Decision: oapi-codegen + Chi Middleware Actual Implementation: swaggo/swag with embedded documentation
Why We Changed
- Framework Compatibility: swaggo works natively with Chi
- Implementation Speed: Days vs weeks of development
- Lower Risk: Proven, battle-tested solution
- Current Needs: Documentation without SDK generation
- Team Capacity: Limited resources for complex integration
What We Actually Need
✅ Documentation: Interactive Swagger UI for API exploration ✅ Tool Integration: Postman/Insomnia/curl support ✅ API Exploration: Try-it-out functionality ✅ Multi-Version Support: Clear v1 vs v2 documentation
❌ SDK Generation: Not currently needed ❌ Type Safety: Manual types sufficient at current scale ❌ OpenAPI 3.0: 2.0 sufficient for documentation ❌ Auto Validation: Manual validation working well
Final Implementation
# 1. Install swaggo
go install github.com/swaggo/swag/cmd/swag@latest
# 2. Add swagger metadata to main.go
// @title dance-lessons-coach API
// @version 1.0
// @description API for dance-lessons-coach service
// @host localhost:8080
// @BasePath /api
package main
Swag Formatting Integration
To ensure consistent swagger comment formatting, we've integrated swag fmt into our workflow:
Git Hooks
Added to .git/hooks/pre-commit:
# Run swag fmt to format swagger comments
echo "Running swag fmt..."
if command -v swag >/dev/null 2>&1; then
swag fmt
if [ $? -ne 0 ]; then
echo "ERROR: swag fmt failed"
exit 1
fi
else
echo "swag not installed, skipping swag fmt"
fi
CI/CD Integration
Added to .gitea/workflows/go-ci-cd.yaml lint-format job:
- name: Install swag
run: go install github.com/swaggo/swag/cmd/swag@latest
- name: Run swag fmt
run: swag fmt
Benefits
- Consistent Formatting: Automatic formatting of swagger comments
- Pre-Commit Validation: Catches issues before commit
- CI/CD Enforcement: Ensures formatting in all pull requests
- Team Consistency: Everyone follows the same rules
- Automatic Fixes: Issues are fixed automatically
Usage
# Format swagger comments manually
swag fmt
# Format is automatically run in:
# - pre-commit hook
# - CI/CD lint-format job
=======
Final Implementation
# 1. Install swaggo
go install github.com/swaggo/swag/cmd/swag@latest
# 2. Add swagger metadata to main.go
// @title dance-lessons-coach API
// @version 1.0
// @description API for dance-lessons-coach service
// @host localhost:8080
// @BasePath /api
package main
Swag Formatting Integration
To ensure consistent swagger comment formatting, we've integrated swag fmt into our workflow:
Git Hooks
Added to .git/hooks/pre-commit:
# Run swag fmt to format swagger comments
echo "Running swag fmt..."
if command -v swag >/dev/null 2>&1; then
swag fmt
if [ $? -ne 0 ]; then
echo "ERROR: swag fmt failed"
exit 1
fi
else
echo "swag not installed, skipping swag fmt"
fi
CI/CD Integration
Added to .gitea/workflows/go-ci-cd.yaml lint-format job:
- name: Install swag
run: go install github.com/swaggo/swag/cmd/swag@latest
- name: Run swag fmt
run: swag fmt
Benefits
- Consistent Formatting: Automatic formatting of swagger comments
- Pre-Commit Validation: Catches issues before commit
- CI/CD Enforcement: Ensures formatting in all pull requests
- Team Consistency: Everyone follows the same rules
- Automatic Fixes: Issues are fixed automatically
Usage
# Format swagger comments manually
swag fmt
# Format is automatically run in:
# - pre-commit hook
# - CI/CD lint-format job
Annotation Placement Considerations
Current Approach: Annotations in cmd/server/main.go
Pros:
- Follows Go community conventions
- Works seamlessly with swaggo
- Clear entry point for API documentation
Cons:
- Mixes API metadata with main package
- Less separation of concerns
Alternative: Annotations in pkg/server/server.go
Pros:
- Better organization (all server code together)
- More logical separation
- Easier maintenance
Cons:
- May require swag configuration changes
- Less conventional
- Potential tooling issues
Decision: Keep annotations in cmd/server/main.go for now
Rationale:
- Follows established community patterns
- Works reliably with current toolchain
- Minimizes risk of breaking changes
- Can revisit if maintenance becomes difficult
Future Consideration: If the project grows significantly, we may revisit this decision and move annotations to the server package for better organization.
What We Actually Implemented
# 3. Annotate handlers and models with hierarchical tags
// @Summary Get personalized greeting
// @Description Returns a greeting with the specified name
// @Tags API/v1/Greeting # Hierarchical tag: Domain/Version/Function
// @Accept json
// @Produce json
// @Param name path string true "Name to greet"
// @Success 200 {object} GreetResponse
// @Failure 400 {object} ErrorResponse
// @Router /v1/greet/{name} [get]
# 4. Generate documentation
go generate ./pkg/server/
# 5. Embed in server and add routes
//go:embed docs/swagger.json
var swaggerJSON embed.FS
// In server setup:
s.router.Handle("/swagger/doc.json", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
data, err := swaggerJSON.ReadFile("docs/swagger.json")
if err != nil {
http.Error(w, "Failed to read swagger.json", http.StatusInternalServerError)
return
}
w.Header().Set("Content-Type", "application/json")
w.Write(data)
}))
s.router.Get("/swagger/*", httpSwagger.WrapHandler)
What We Did NOT Implement (From Original Plan)
# NOT IMPLEMENTED: oapi-codegen approach
# 1. Install oapi-codegen
# go install github.com/deepmap/oapi-codegen/cmd/oapi-codegen@latest
# 2. Create OpenAPI spec (openapi.yaml)
# openapi: 3.0.3
# info:
# title: dance-lessons-coach API
# version: 1.0.0
# 3. Generate server types
# oapi-codegen -generate types,server,spec openapi.yaml > pkg/api/gen_api.go
# 4. Add Chi middleware
# r.Use(middleware.OapiRequestValidator(swagger))
### Implementation
# 3. Annotate handlers and models with hierarchical tags
// @Summary Get personalized greeting
// @Description Returns a greeting with the specified name
// @Tags API/v1/Greeting # Hierarchical tag: Domain/Version/Function
// @Accept json
// @Produce json
// @Param name path string true "Name to greet"
// @Success 200 {object} GreetResponse
// @Failure 400 {object} ErrorResponse
// @Router /v1/greet/{name} [get]
# Example v2 endpoint with hierarchical tag
// @Summary Get greeting (v2)
// @Description Returns a greeting message with validation (v2)
// @Tags API/v2/Greeting # Hierarchical tag: Domain/Version/Function
// @Accept json
// @Produce json
// @Param request body GreetRequest true "Greeting request"
// @Success 200 {object} GreetResponseV2
// @Failure 400 {object} ValidationError
// @Router /v2/greet [post]
# System endpoints use different domain
// @Summary Health check
// @Description Check if the service is healthy
// @Tags System/Health # Hierarchical tag: Domain/Function
// @Accept json
// @Produce json
// @Success 200 {object} map[string]string "Service is healthy"
// @Router /health [get]
func (h *apiV1GreetHandler) handleGreetPath(w http.ResponseWriter, r *http.Request) {
// handler implementation
}
## Tagging Strategy Evolution
### Phase 1: Simple Tags (Initial Approach)
```go
// @Tags greet
// @Tags health
Issue: No version separation, all endpoints mixed together
Phase 2: Version-Based Tags (Improved)
// @Tags v1-greet
// @Tags v2-greet
// @Tags health
Issue: Still flat structure, no domain separation
Phase 3: Hierarchical Tags (Current - Recommended)
// @Tags API/v1/Greeting
// @Tags API/v2/Greeting
// @Tags System/Health
Benefits:
- Clear domain separation (API vs System)
- Version organization within domains
- Natural hierarchy in Swagger UI
- Scalable for future growth
Hierarchical Tagging Best Practices
1. Domain-First Organization
API/for business endpointsSystem/for infrastructure endpointsAdmin/for administrative endpointsInternal/for internal-only endpoints
2. Consistent Structure
- API Domain:
API/{version}/{function} - System Domain:
System/{function} - Admin Domain:
Admin/{function}
3. Versioning Strategy
- Use semantic versions:
v1,v2,v3 - Keep versions consistent across domains
- Document breaking changes clearly
4. Function Naming
- Use singular nouns:
Greeting,User,Order - Be specific:
PaymentProcessingvsPayment - Avoid acronyms unless widely known
4. Generate documentation
go generate ./pkg/server/
5. Embed in server and add routes
//go:embed docs/swagger.json var swaggerJSON embed.FS
// In server setup: s.router.Handle("/swagger/doc.json", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { data, err := swaggerJSON.ReadFile("docs/swagger.json") if err != nil { http.Error(w, "Failed to read swagger.json", http.StatusInternalServerError) return } w.Header().Set("Content-Type", "application/json") w.Write(data) }))
s.router.Get("/swagger/*", httpSwagger.WrapHandler)
### Implementation Plan
```bash
# 1. Install oapi-codegen
go install github.com/deepmap/oapi-codegen/cmd/oapi-codegen@latest
# 2. Create OpenAPI spec (openapi.yaml)
openapi: 3.0.3
info:
title: dance-lessons-coach API
version: 1.0.0
description: API for dance-lessons-coach service
servers:
- url: http://localhost:8080/api
description: Development server
# 3. Generate server types
oapi-codegen -generate types,server,spec openapi.yaml > pkg/api/gen_api.go
# 4. Add Chi middleware
r.Use(middleware.OapiRequestValidator(swagger))
Pros and Cons of the Options
Option 1: swaggo/swag (NOW CHOSEN)
- Good: Simple, popular, good Go integration, mature, easy to use, works well with Chi
- Bad: OpenAPI 2.0 primarily, annotation-based can be fragile, less expressive
- Decision: Best balance of simplicity and functionality for current needs
Option 2: go-swagger
- Good: Mature, full-featured, code generation, OpenAPI 2.0
- Bad: Complex setup, slower development, less Chi-specific integration
- Decision: Overkill for current simple API needs
Option 3: oapi-codegen (PREVIOUSLY CHOSEN)
- Good: OpenAPI 3.0+, type-safe, active development
- Bad: Echo framework dependency, poor Chi integration, complex workarounds needed
- Decision: Too complex for current needs, better suited for larger projects
Option 4: bufbuild/connect
- Good: Modern, gRPC+REST, performance, future-proof
- Bad: Architectural change, steep learning curve, overkill for simple REST API
- Decision: Not appropriate for current REST-focused architecture
Trade-offs Analysis (Explicit)
What We Gain (✅ Positive Trade-offs)
- Framework Compatibility: Native Chi router support
- Implementation Speed: Days vs weeks of development
- Lower Risk: Proven, battle-tested solution
- Simpler Maintenance: No custom adapters to maintain
- Team Familiarity: Easier learning curve
- Immediate Deployment: Solves documentation needs now
- Proven Ecosystem: Widely used in production
What We Give Up (❌ Negative Trade-offs)
-
OpenAPI 3.0 Features:
- Webhooks and callbacks
- Links between operations
- More expressive schemas
- Better reuse capabilities
-
Automatic Type Generation:
- No generated Go types from spec
- Manual type definitions required
- Potential for type mismatches
-
Built-in Validation:
- No automatic request validation
- Manual validation implementation
- More boilerplate code
-
SDK Generation:
- No automatic client generation
- Manual client development
- Potential inconsistency between clients and API
-
Future-Proofing:
- OpenAPI 2.0 is deprecated
- May need migration later
- Industry moving to 3.0+
Trade-off Evaluation
Critical Needs (Must Have):
- ✅ Interactive Documentation: swaggo provides this
- ✅ API Exploration: swaggo provides this
- ✅ Tool Integration: swaggo provides this
- ✅ Multi-Version Support: swaggo provides this
Nice-to-Haves (Can Defer):
- ❌ SDK Generation: Not needed currently
- ❌ Type Safety: Manual types sufficient
- ❌ OpenAPI 3.0: 2.0 sufficient for docs
- ❌ Auto Validation: Manual validation working
Cost-Benefit Analysis
Cost of swaggo Approach:
- 1-2 days implementation
- OpenAPI 2.0 limitation
- Manual type definitions
- Future migration possibility
Benefit of swaggo Approach:
- Solves immediate documentation needs
- Zero breaking changes
- Low maintenance overhead
- Team can focus on features
- Proven reliability
Cost of oapi-codegen Approach:
- 3-5 weeks implementation (with Echo migration)
- OR 2-3 weeks custom adapter development
- High risk of bugs
- Team learning curve
- Potential service disruption
Benefit of oapi-codegen Approach:
- OpenAPI 3.0 compliance
- Automatic type generation
- Built-in validation
- Future SDK generation
- Industry standard compliance
Decision Justification
The trade-offs favor swaggo because:
- 80/20 Rule: Get 80% of value with 20% of effort
- Current Scale: Small API doesn't need advanced features
- Team Capacity: Limited resources better spent on features
- Risk Profile: Cannot justify high-risk migration
- Business Needs: Documentation solves immediate problems
- Future Flexibility: Can migrate later if needs change
When to reconsider oapi-codegen:
- API grows to 50+ endpoints
- Need automatic SDK generation
- Adding microservices architecture
- Team has capacity for migration
- OpenAPI 2.0 becomes limiting
Migration Path Forward
Current Phase (0-6 months):
- Implement swaggo for documentation
- Monitor API growth and complexity
- Document OpenAPI 3.0 requirements
Future Phase (6-12 months):
- Re-evaluate if API complexity increases
- Consider oapi-codegen if SDK generation needed
- Evaluate migration cost vs benefit
- Plan migration as part of larger updates
Long-Term (12+ months):
- Full OpenAPI 3.0 migration if justified
- Consider framework changes if needed
- Align with other architectural changes
- Budget time and resources appropriately
Final Trade-off Summary
| Aspect | swaggo | oapi-codegen | Decision |
|---|---|---|---|
| Documentation | ✅ Excellent | ✅ Excellent | ✅ Both good |
| Tool Integration | ✅ Good | ✅ Excellent | ✅ swaggo sufficient |
| Implementation Time | 1-2 days | 2-5 weeks | ✅ swaggo wins |
| Risk | Low | High | ✅ swaggo wins |
| Maintenance | Simple | Moderate | ✅ swaggo wins |
| Type Safety | ❌ Manual | ✅ Auto | ❌ Not critical |
| SDK Generation | ❌ Manual | ✅ Auto | ❌ Not needed |
| OpenAPI Version | 2.0 | 3.0+ | ❌ 2.0 sufficient |
| Validation | ❌ Manual | ✅ Auto | ❌ Existing works |
| Team Learning | Minimal | Significant | ✅ swaggo wins |
Result: swaggo wins 7-2 on critical factors
Comparison with Original Choice
What we gain by switching to swaggo:
- ✅ Actually works with Chi router: Native compatibility vs complex adapters
- ✅ Simpler to implement and maintain: No framework adaptation needed
- ✅ Faster to deploy: Days vs weeks of development time
- ✅ Better community support for Chi: Proven Chi integration patterns
- ✅ Reduced risk: Mature, battle-tested solution
What we lose:
- ❌ OpenAPI 3.0 features: But 2.0 is sufficient for current needs
- ❌ Automatic type generation: Manual types are manageable at current scale
- ❌ Built-in validation: Can add custom validation later if needed
- ❌ Future-proof standard: Practical solution for current requirements
Root Cause Analysis: The original decision failed to properly evaluate:
- Framework Compatibility: oapi-codegen's Echo dependency was underestimated
- Integration Complexity: Adapter development effort was not considered
- Team Bandwidth: Maintenance burden of custom adapters
- Project Scale: Over-engineering for current simple API needs
Verdict: The trade-offs are worth it for our current needs and team size. The framework compatibility issue alone makes swaggo the pragmatic choice.
Framework Selection Lessons
For Future ADRs:
- Framework First: Evaluate framework compatibility before features
- Pragmatic Choices: Consider team size and maintenance capacity
- Implementation Testing: Prove concepts before finalizing decisions
- Risk Assessment: Evaluate integration complexity, not just features
- Scale Appropriateness: Match tool complexity to project scale
Decision Quality Checklist:
- Features and capabilities
- Framework compatibility ✓ (Added in revision)
- Community support
- Implementation complexity ✓ (Added in revision)
- Long-term maintenance
- Team expertise match ✓ (Added in revision)
Mitigations
- Gradual Adoption: Start with v2 API, migrate v1 later
- Documentation: Comprehensive team training
- Automation: Integrate into build scripts
- Templates: Create OpenAPI spec templates
- CI/CD: Add OpenAPI validation to pipeline
Verification
Acceptance Criteria
- ✅ Generate OpenAPI spec for existing APIs
- ✅ Integrate Swagger UI at
/api/docs - ✅ Add request validation middleware
- ✅ Generate TypeScript client from spec
- ✅ Pass OpenAPI linting in CI/CD
- ✅ Maintain backward compatibility
Test Plan
# 1. Generate spec
oapi-codegen openapi.yaml
# 2. Start server with Swagger UI
./scripts/start-server.sh start
# 3. Access Swagger UI
open http://localhost:8080/api/docs
# 4. Test validation
curl -X POST http://localhost:8080/api/v2/greet \
-H "Content-Type: application/json" \
-d '{"invalid_field": "test"}' # Should return 400
# 5. Generate client
oapi-codegen -generate typescript-client openapi.yaml > client.ts
Related Decisions
- ADR-0002: Chi Router - Routing framework
- ADR-0010: API v2 Feature Flag - API versioning
- ADR-0011: Validation Library - Input validation
- ADR-0009: Hybrid Testing - Testing strategy
Future Considerations
- Migration Path: Gradually migrate v1 API to OpenAPI
- Spec Management: Establish OpenAPI spec governance
- Client SDKs: Generate and publish official SDKs
- API Gateway: Consider integration with API gateways
- Performance: Monitor validation middleware overhead
- Tooling: Explore additional oapi-codegen features
Revision History
- 1.0 (2026-04-05): Initial proposal (oapi-codegen)
- 1.1 (2026-04-05): Added implementation details and verification plan
- 2.0 (2026-04-05): MAJOR REVISION - Switched to swaggo/swag due to integration challenges
- Changed from oapi-codegen to swaggo
- Updated implementation plan
- Revised pros/cons analysis
- Added migration considerations
- 2.1 (2026-04-05): CRITICAL ADDITION - Defined expected outcomes and requirements analysis
- Added "Expected Outcomes" section
- Defined priority matrix for requirements
- Clarified what we actually need vs nice-to-haves
- Validated decision against actual requirements
- Added framework compatibility analysis
References
Conclusion
What We Actually Implemented
✅ OpenAPI Documentation: Comprehensive API documentation with swaggo/swag
✅ Interactive Swagger UI: Available at /swagger/ for API exploration
✅ Embedded Specification: Single-binary deployment with embedded OpenAPI spec
✅ Hierarchical Tagging: Clear organization with Domain/Version/Function tags
✅ Production Ready: Fully tested and operational
What We Did NOT Implement (From Original Plan)
❌ oapi-codegen: Using swaggo instead due to Chi compatibility ❌ SDK Generation: No generated Go/TypeScript/Python clients ❌ OpenAPI 3.0: Using OpenAPI 2.0 (sufficient for current needs) ❌ Auto Validation: Manual validation working well ❌ Type Safety: Manual types sufficient at current scale
Why This is the Right Approach
- Framework Compatibility: swaggo works natively with Chi router
- Implementation Speed: Days vs weeks of development
- Lower Risk: Proven, battle-tested solution
- Current Needs: Documentation without SDK generation
- Team Capacity: Limited resources for complex integration
Future Migration Path
If we need SDK generation in the future:
- Add oapi-codegen alongside swaggo (not instead of)
- Generate SDKs from OpenAPI spec
- Add SDK-based testing
- Implement request validation middleware
- Migrate to OpenAPI 3.0 if needed
Current Status: ✅ Implemented (OpenAPI documentation; SDK generation out of scope) Implementation: swaggo/swag with embedded documentation Documentation: http://localhost:8080/swagger/ OpenAPI Spec: http://localhost:8080/swagger/doc.json Swagger UI: http://localhost:8080/swagger/index.html
Proposed by: Arcodange Team Implemented by: 2026-04-05 Last Updated: 2026-04-05 Status: Production Ready for Current Documentation Needs
Note: This ADR has been updated to reflect the actual implementation (swaggo/swag) rather than the originally proposed approach (oapi-codegen). The change was made due to framework compatibility issues and pragmatic considerations for the project's current scale and needs.