1. Code Generation Pipeline
Author: System Architecture Team
Status: Active
Last Updated: 2025-06-14
Goals
- Provide a comprehensive understanding of the code generation pipeline
- Explain dependencies between different generation tools
- Document the build process for new developers
- Establish patterns for extending the generation pipeline
Non-Goals
- Detailed tool-specific configuration (covered in individual tool docs)
- Alternative build systems or CI/CD pipeline design
- Performance optimization of the build process
Overview
The Facility Reservation System uses a sophisticated code generation pipeline that transforms declarative specifications into type-safe Go code. The pipeline consists of two parallel tracks:
- API Track: TypeSpec → OpenAPI → Go HTTP handlers
- Database Track: SQL Schema → Go database code + migrations
These tracks are orchestrated by the make build_dev
command, which ensures proper dependency ordering and validates the entire system.
Detailed Design
Pipeline Architecture
graph TB
subgraph "Source Files"
TSP[spec/main.tsp<br/>TypeSpec API Spec]
SQL[_db/schema.sql<br/>SQL Schema]
Queries[_db/query_*.sql<br/>SQL Queries]
end
subgraph "Generation Tools"
TSPCompile[tsp compile<br/>TypeSpec Compiler]
Ogen[ogen<br/>OpenAPI Generator]
SQLCGen[sqlc generate<br/>SQL Code Generator]
Atlas[atlas schema apply<br/>Migration Tool]
end
subgraph "Generated Code"
OpenAPI[spec/tsp-output/*.yaml<br/>OpenAPI Schema]
APICode[api/*.go<br/>HTTP Handlers]
DBCode[internal/db/*.go<br/>Database Code]
Migration[Database Migration]
end
subgraph "Application"
Service[internal/api_service.go<br/>Business Logic]
Binary[bin/api-server<br/>Compiled Binary]
end
TSP --> TSPCompile
TSPCompile --> OpenAPI
OpenAPI --> Ogen
Ogen --> APICode
SQL --> SQLCGen
SQL --> Atlas
Queries --> SQLCGen
SQLCGen --> DBCode
Atlas --> Migration
APICode --> Service
DBCode --> Service
Service --> Binary
Build Command Breakdown
The build pipeline executes these steps in order:
- Clean previous artifacts
- Format and lint code
- Generate database code
- Generate API code
- Run tests
- Build binary
For complete build commands and workflow, see Development Workflow.
API Generation Track
Step 1: TypeSpec Compilation
cd ./spec && tsp compile .
- Input:
spec/main.tsp
(TypeSpec specification) - Output:
spec/tsp-output/schema/3.1.0/openapi.yaml
(OpenAPI v3.1) - Validation: TypeSpec compiler checks type consistency and API design
Step 2: Go Server Generation
ogen -target api -package api --clean ./spec/tsp-output/schema/3.1.0/openapi.yaml
- Input: Generated OpenAPI YAML
- Output:
api/*.go
files (handlers, schemas, validation) - Features: Type-safe HTTP handlers, request/response serialization, input validation
Database Generation Track
Step 1: Query Code Generation
sqlc generate
- Input:
_db/schema.sql
+_db/query_*.sql
- Output:
internal/db/*.go
(type-safe query functions) - Features: Compile-time SQL validation, type-safe parameter binding
Step 2: Schema Migration (development only)
- Input:
_db/schema.sql
- Output: Updated database schema
- Features: Automatic migration generation, schema drift detection
Integration Points
Service Layer Integration
// internal/api_service.go connects generated code
type Service struct {
api.UnimplementedHandler // Generated API interface
db *DatabaseService // Generated database queries
}
Testing Integration
- Unit tests use mocked database interfaces
- Integration tests use real database with generated schema
- Generated validation ensures consistency between tests and production
Alternatives Considered
Alternative 1: Manual HTTP Handlers
Pros: Direct control, no compilation step
Cons: Boilerplate code, no compile-time API validation
Rejected: Type safety and consistency benefits outweigh complexity
Alternative 2: Traditional ORM
Pros: Familiar patterns, automatic relationships
Cons: Runtime query generation, performance unpredictability
Rejected: ADR-002 documents detailed rationale
Alternative 3: Single-Stage Build
Pros: Simpler build process
Cons: No incremental compilation, harder to debug failures
Rejected: Multi-stage approach provides better error messages and faster iteration
Testing Strategy
Build Pipeline Tests
- Unit Tests: Each generation tool validates its inputs
- Integration Tests: End-to-end build process validation
- Smoke Tests: Generated code compiles and basic functionality works
Generated Code Tests
- API Tests: Generated handlers respond correctly to valid/invalid inputs
- Database Tests: Generated queries produce expected results
- Type Safety Tests: Compile-time verification of type consistency
Build Process Validation
# Clean build test
make clean && make build_dev
# Incremental build test
touch spec/main.tsp && make build_dev
# Error handling test
# (introduce syntax error and verify clear error messages)
Future Considerations
Potential Improvements
- Build Caching: Cache generated artifacts for faster incremental builds
- Parallel Generation: Run API and database tracks in parallel
- Better Error Messages: Improve error correlation between tools
- IDE Integration: Better development experience with generated code
Scalability Considerations
- Multiple APIs: Support for additional TypeSpec specifications
- Database Sharding: Schema generation for multiple databases
- Client Generation: Automate client SDK generation for external consumers
Known Limitations
- Tool Dependencies: Requires specific versions of generation tools
- Debug Complexity: Errors may span multiple generation stages
- Learning Curve: New developers need to understand entire pipeline
Migration Path
If the pipeline needs significant changes:
- Implement new approach alongside existing system
- Migrate one component at a time (API or database)
- Validate each step with comprehensive testing
- Document migration process in new design doc