Go (Golang) — "Max Power" Agentic Backend Architecture
AI Context & Token Optimization
- Compile-Time Dependency Injection: Use Uber Fx. This forces explicit dependency declaration, giving the AI instant compiler feedback if a module is wired incorrectly, preventing runtime panics.
- gRPC Source of Truth: Rely on
.protofiles as the absolute contract. The AI uses these to perfectly align client/server interactions. - No Reflection ORMs: Use
pgxorent. Reflection-heavy ORMs like GORM cause unpredictable runtime behaviors that confuse AI debugging workflows.
Modern Project Initiation Guide
When scaffolding a high-performance backend (such as a sub-50ms latency Caller ID system), enforce the following strict rules to maximize AI reasoning and eliminate runtime magic:
- Hexagonal Architecture (Ports & Adapters): Strictly separate the core business logic (Domain) from external concerns (gRPC, PostgreSQL, Redis). The Domain layer must have zero dependencies on external libraries.
- gRPC-First: Use protocol buffers (
.proto) as the single source of truth for all API contracts. Generate Go interfaces from proto files. - Compile-Time DI: Use Uber Fx or Google Wire for dependency injection. This forces the AI to explicitly declare dependencies, resulting in instant compile-time feedback if a dependency is missing.
- Caching Layer: Implement Redis via the
go-redisclient for all read-heavy, low-latency lookups (e.g., spam number checks). - Database: Use PostgreSQL with
pgxfor raw, high-performance database interactions, orent(by Facebook) if a strongly-typed ORM is required. Avoid reflection-heavy ORMs like GORM.
Project Structure
project-root/
├── cmd/
│ └── server/
│ └── main.go # Entry point, initializes Uber Fx application
├── internal/
│ ├── core/
│ │ ├── domain/ # Pure Go structs (e.g., PhoneNumber.go)
│ │ └── ports/ # Interfaces (e.g., CacheRepository, CallService)
│ ├── application/ # Use cases implementing inbound ports
│ │ └── call_service.go
│ ├── adapters/
│ │ ├── inbound/
│ │ │ └── grpc/ # gRPC handlers implementing proto generated interfaces
│ │ └── outbound/
│ │ ├── postgres/ # PostgreSQL repository implementations
│ │ └── redis/ # Redis cache repository implementations
│ └── di/ # Uber Fx module providers
├── api/
│ └── proto/ # .proto definition files
├── pkg/ # Shared libraries (logging, metrics)
├── go.mod
└── Makefile # Protoc generation and build commands
Naming Conventions
| Artifact | Convention | Example |
|---|---|---|
| Interfaces (Ports) | Nouns ending in er |
CallReader, CacheWriter |
| Structs | PascalCase |
CallScreeningRequest |
| Files | snake_case.go |
call_service.go |
| DI Providers | Prefix with New |
NewPostgresRepository |
Architectural Patterns
Functional Options Pattern
Use this for configuring complex structures (like external client adapters) cleanly:
type Server struct { ... }
type Option func(*Server)
func WithTimeout(t time.Duration) Option {
return func(s *Server) { s.timeout = t }
}
Error Handling (Self-Healing for AI)
Never panic. Return errors explicitly. Wrap errors with context using %w so the AI agent reading the logs can trace the exact failure path:
return fmt.Errorf("redis cache miss for number %s: %w", number, err)
Universal DateTime Governance
- UTC Storage: All
time.Timefields in domain structs MUST usetime.Now().UTC(). Banned: baretime.Now(). - Clock Interface: Define a
Clockinterface (Now() time.Time) in thecore/portspackage. Default implementation usestime.Now().UTC(). Inject via Uber Fx. Banned: callingtime.Now()directly in application/domain code. - gRPC Proto: Use
google.protobuf.Timestamp(always UTC) in.protofiles. Never send raw Unix ints over gRPC unless the proto explicitly defines anint64field for epoch ms. - Redis Cache: Store epoch ms (int64) in Redis for time-based values. Never store formatted date strings in cache keys or values.
Testing Strategies
| Layer | Test Type | Framework / Tools |
|---|---|---|
| Application | Unit | testing + testify/assert + mockery (for ports) |
| Adapters | Integration | testcontainers-go (spin up real Redis/PG in Docker) |
- Generate mocks for all interfaces in
internal/core/portsusingmockery. - AI must write table-driven tests (
[]struct) for all core business logic.