- Designed trunk-based development workflow with branch protection - Added workflow validation job to prevent main branch breaks - Integrated act (GitHub Actions runner) for local Gitea workflow testing - Created unified CI/CD script interface (scripts/cicd.sh) - Added YAML lint configuration with practical limits (400 chars) - Organized all CI/CD scripts under scripts/cicd/ directory - Confirmed Gitea/GitHub Actions compatibility via local testing - Updated ADR 0017 with implementation details and test results - Enhanced documentation with local development workflow See ADR-0017 for complete trunk-based development workflow documentation. See ADR-0016 for CI/CD pipeline design.
7.5 KiB
14. gRPC Adoption Strategy
Date: 2026-04-05 Status: Accepted Authors: Arcodange Team
Context
The DanceLessonsCoach project currently uses REST/JSON for all API communication. As the project evolves, we need to determine when and how to adopt gRPC for performance-critical and internal communication scenarios.
Decision Drivers
- Current Needs: Simple API with good REST support
- Future Growth: Potential for mobile apps and microservices
- Performance: Current REST performance is adequate
- Complexity: gRPC adds significant architectural complexity
- Team Expertise: Strong REST/JSON experience, limited gRPC experience
- Ecosystem: Existing tooling and documentation for REST
Considered Options
Option 1: Immediate Full gRPC Adoption
Description: Replace all REST endpoints with gRPC immediately
Pros:
- Future-proof architecture
- Best performance from day one
- Clean slate design
Cons:
- Significant development effort
- Steep learning curve
- Breaking changes for existing clients
- Overkill for current needs
Option 2: Hybrid Approach (Recommended)
Description: Keep REST for public API, add gRPC for internal/services
Pros:
- Backward compatibility maintained
- Gradual learning curve
- Performance where needed
- Flexibility for future growth
Cons:
- More complex architecture
- Need to maintain both protocols
- Gateway translation overhead
Option 3: REST Only
Description: Continue with REST/JSON only
Pros:
- Simple and well-understood
- Good tooling and debugging
- No architectural changes needed
Cons:
- May limit future scalability
- Performance ceiling
- Harder to add real-time features
Option 4: gRPC for New Features Only
Description: Use REST for existing, gRPC for new features
Pros:
- No breaking changes
- Learn gRPC gradually
- Performance for new features
Cons:
- Inconsistent API surface
- Complex migration path
- Harder to maintain coherence
Decision Outcome
Chosen option: Option 2 - Hybrid Approach
Implementation Strategy
Phase 1: Preparation (Current)
- ✅ Document gRPC adoption strategy (this ADR)
- ✅ Implement OpenAPI/Swagger for REST (ADR-0013)
- ✅ Continue REST development
- ✅ Monitor performance metrics
Phase 2: Foundation (When Needed)
# Add gRPC dependencies
go get google.golang.org/grpc
go get google.golang.org/protobuf
# Create proto directory
mkdir -p proto/greet
# Add basic protobuf definition
cat > proto/greet/greet.proto << 'EOF'
syntax = "proto3";
package greet.v1;
service GreetService {
rpc Greet (GreetRequest) returns (GreetResponse);
}
message GreetRequest {
string name = 1;
}
message GreetResponse {
string message = 1;
}
EOF
Phase 3: Internal Services (Future)
// When adding internal services:
// User Service <--gRPC--> Greet Service
// Analytics Service <--gRPC--> Greet Service
func (s *Server) startGRPC() {
if s.config.GRPC.Enabled {
lis, err := net.Listen("tcp", s.config.GRPC.Address)
if err != nil {
log.Error().Err(err).Msg("Failed to listen for gRPC")
return
}
grpcServer := grpc.NewServer()
proto.RegisterGreetServiceServer(grpcServer, s.grpcHandler)
log.Info().Str("address", s.config.GRPC.Address).Msg("Starting gRPC server")
if err := grpcServer.Serve(lis); err != nil {
log.Error().Err(err).Msg("gRPC server failed")
}
}
}
Phase 4: Mobile Clients (Future)
# When adding mobile apps:
# iOS/Android App --gRPC--> DanceLessonsCoach
# Generate mobile clients
protoc --plugin=protoc-gen-grpc=`which grpc_swift_plugin` \
--grpc_swift_out=. \
proto/greet/greet.proto
Consequences
Positive
- Backward Compatibility: Existing REST clients continue working
- Performance: gRPC available when needed for critical paths
- Flexibility: Can choose right protocol for each use case
- Gradual Learning: Team can learn gRPC at appropriate pace
- Future-Proof: Architecture ready for growth
Negative
- Complexity: More moving parts to maintain
- Overhead: Gateway translation between protocols
- Learning Curve: Team needs to learn gRPC eventually
- Build Complexity: Additional build steps for protobuf
Mitigations
- Documentation: Comprehensive gRPC guides and examples
- Training: Gradual team education on gRPC concepts
- Tooling: Automate protobuf generation in CI/CD
- Monitoring: Track protocol usage and performance
Verification
Success Criteria
- ✅ REST API remains fully functional
- ✅ gRPC can be enabled via configuration
- ✅ No performance regression in REST paths
- ✅ Clear documentation for both protocols
- ✅ CI/CD supports both REST and gRPC testing
Test Plan
# Test REST still works
curl http://localhost:8080/api/v1/greet/John
# Expected: {"message":"Hello John!"}
# Test gRPC can be disabled by default
export DLC_GRPC_ENABLED=false
./bin/server
# Expected: Only REST server starts
# Test configuration validation
DLC_GRPC_ENABLED=true DLC_GRPC_PORT=invalid ./bin/server
# Expected: Configuration error, clean exit
Related Decisions
- ADR-0002: Chi Router - Current routing framework
- ADR-0013: OpenAPI/Swagger - REST documentation
- ADR-0010: API v2 Feature Flag - Versioning strategy
Future Triggers
Consider implementing gRPC when any of these occur:
- Mobile App Development: Need for efficient mobile communication
- Microservices: Adding internal services that need gRPC
- Performance Issues: REST becomes bottleneck at scale
- Real-time Features: Need for streaming/bidirectional communication
- Team Readiness: Team comfortable with gRPC concepts
Revision History
- 1.0 (2026-04-05): Initial decision
- 1.1 (2026-04-05): Added implementation phases and triggers
References
Approved by: Arcodange Team Effective Date: 2026-04-05
Configuration Reference
# config.yaml example for future gRPC support
grpc:
enabled: false # Set to true to enable gRPC server
host: "0.0.0.0"
port: "50051"
reflection: true # Enable for development
max_msg_size: 4194304 # 4MB max message size
rest:
enabled: true # REST remains enabled
host: "0.0.0.0"
port: "8080"
Migration Checklist
- Add gRPC dependencies to go.mod
- Create proto directory structure
- Add basic greet.proto definition
- Implement gRPC server (disabled by default)
- Add configuration options
- Update CI/CD for protobuf generation
- Add gRPC health checks
- Document gRPC usage
- Performance benchmarking
- Gradual rollout to production
Monitoring Metrics
Recommended metrics to track:
# REST metrics
rest_requests_total{endpoint="/api/v1/greet", status="200"}
rest_response_time_seconds{quantile="0.95"}
# gRPC metrics (when enabled)
grpc_server_handling_seconds{grpc_method="Greet", grpc_code="OK"}
grpc_server_started_total{grpc_method="Greet"}
# Comparison metrics
api_latency_comparison{protocol="rest", endpoint="/greet"}
api_latency_comparison{protocol="grpc", endpoint="/greet"}