| name | golang-rules |
| description | Comprehensive Go coding rules covering style, patterns, testing, and security |
Go Coding Rules
Coding Style
Immutability (CRITICAL)
ALWAYS create new objects, NEVER mutate existing ones:
// Pseudocode
WRONG: modify(original, field, value) -> changes original in-place
CORRECT: update(original, field, value) -> returns new copy with change
Rationale: Immutable data prevents hidden side effects, makes debugging easier, and enables safe concurrency.
File Organization
MANY SMALL FILES > FEW LARGE FILES:
- High cohesion, low coupling
- 200-400 lines typical, 800 max
- Extract utilities from large modules
- Organize by feature/domain, not by type
Formatting
- gofmt and goimports are mandatory -- no style debates
Design Principles
- Accept interfaces, return structs
- Keep interfaces small (1-3 methods)
Error Handling
ALWAYS handle errors comprehensively:
- Handle errors explicitly at every level
- Provide user-friendly error messages in UI-facing code
- Log detailed error context on the server side
- Never silently swallow errors
Always wrap errors with context:
if err != nil {
return fmt.Errorf("failed to create user: %w", err)
}
Input Validation
ALWAYS validate at system boundaries:
- Validate all user input before processing
- Use schema-based validation where available
- Fail fast with clear error messages
- Never trust external data (API responses, user input, file content)
Code Quality Checklist
Before marking work complete:
Patterns
Skeleton Projects
When implementing new functionality:
- Search for battle-tested skeleton projects
- Evaluate options for security, extensibility, relevance
- Clone best match as foundation
- Iterate within proven structure
Repository Pattern
Encapsulate data access behind a consistent interface:
- Define standard operations: findAll, findById, create, update, delete
- Concrete implementations handle storage details (database, API, file, etc.)
- Business logic depends on the abstract interface, not the storage mechanism
- Enables easy swapping of data sources and simplifies testing with mocks
API Response Format
Use a consistent envelope for all API responses:
- Include a success/status indicator
- Include the data payload (nullable on error)
- Include an error message field (nullable on success)
- Include metadata for paginated responses (total, page, limit)
Functional Options
type Option func(*Server)
func WithPort(port int) Option {
return func(s *Server) { s.port = port }
}
func NewServer(opts ...Option) *Server {
s := &Server{port: 8080}
for _, opt := range opts {
opt(s)
}
return s
}
Small Interfaces
Define interfaces where they are used, not where they are implemented.
Dependency Injection
Use constructor functions to inject dependencies:
func NewUserService(repo UserRepository, logger Logger) *UserService {
return &UserService{repo: repo, logger: logger}
}
Testing
Minimum Test Coverage: 80%
Test Types (ALL required):
- Unit Tests - Individual functions, utilities, components
- Integration Tests - API endpoints, database operations
- E2E Tests - Critical user flows
Test-Driven Development
MANDATORY workflow:
- Write test first (RED)
- Run test - it should FAIL
- Write minimal implementation (GREEN)
- Run test - it should PASS
- Refactor (IMPROVE)
- Verify coverage (80%+)
Troubleshooting Test Failures
- Check test isolation
- Verify mocks are correct
- Fix implementation, not tests (unless tests are wrong)
Framework
Use the standard go test with table-driven tests.
Race Detection
Always run with the -race flag:
go test -race ./...
Coverage
go test -cover ./...
Security
Mandatory Security Checks
Before ANY commit:
Secret Management
- NEVER hardcode secrets in source code
- ALWAYS use environment variables or a secret manager
- Validate that required secrets are present at startup
- Rotate any secrets that may have been exposed
apiKey := os.Getenv("OPENAI_API_KEY")
if apiKey == "" {
log.Fatal("OPENAI_API_KEY not configured")
}
Security Scanning
- Use gosec for static security analysis:
gosec ./...
Context & Timeouts
Always use context.Context for timeout control:
ctx, cancel := context.WithTimeout(ctx, 5*time.Second)
defer cancel()
Security Response Protocol
If security issue found:
- STOP immediately
- Fix CRITICAL issues before continuing
- Rotate any exposed secrets
- Review entire codebase for similar issues