- Every function that returns (T, error) must have the error checked immediately on the next line. Never use
_to ignore errors. Useif err != nil { return err }orif err != nil { return fmt.Errorf("context: %w", err) }. - All error returns must wrap context using
fmt.Errorf("operation description: %w", err). Never return bareerr. - Custom error types must be defined as structs with an
Error()string method. Useerrors.Is()anderrors.As()for type checking, never type assertions on error. - HTTP handlers must return typed errors. Use a centralized error response function that logs and writes JSON with status code, error code, and message.
- Goroutine functions must not panic. Wrap goroutine bodies in
defer func() { if r := recover(); r != nil { logger.Error("panic", "value", r) } }(). - Database operations must have explicit timeout contexts passed as first parameter. Never use
context.Background()for DB calls. - All error logging must use
slog.Error()with structured fields. Never usefmt.Println(),log.Println(), orfmt.Printf()for errors.
- All logging must use the
slogpackage. Import as"log/slog". Never import"log"or usefmt.Print*for logging. - Every log statement must include a message string as first argument after logger method. Use structured fields for all contextual data:
logger.Info("user login", "user_id", userID, "ip", ipAddr). - Log levels must be:
Debugfor development details,Infofor business events,Warnfor recoverable issues,Errorfor failures that need attention. - Never log sensitive data: passwords, tokens, API keys, PII. If logging user data, hash or truncate it.
- Every function that performs I/O, calls other services, or runs in a goroutine must accept
ctx context.Contextas the first parameter. - All context-accepting functions must respect context cancellation. Check
ctx.Done()in loops and before long operations. - Goroutines spawned in request handlers must be tracked with
sync.WaitGroupor channels. Never spawn fire-and-forget goroutines in HTTP handlers. - Shared mutable state must be protected by
sync.Mutexorsync.RWMutex. Never access maps, slices, or struct fields concurrently without synchronization. - All blocking operations (network, file I/O, database) must have timeouts. Use
context.WithTimeout(ctx, 30*time.Second)before calling blocking functions.
- Package names must be lowercase, single word, no underscores. Example:
auth,models,handlers— notauth_serviceorAuthService. - Exported functions and types must use PascalCase. Unexported must use camelCase. Example:
func (s *Service) GetUser()notfunc (s *Service) get_user(). - Interface names must end with
er. Example:Reader,Writer,Handler,Logger— notIReaderorReadInterface. - Variable names must be concise but clear. Single-letter variables only for loop counters (
i,j) or receiver names (r,wfor http.ResponseWriter). Never useufor user ordfor data. - File names must be lowercase with underscores for word separation. Example:
user_service.go,http_handler.go— notUserService.gooruserservice.go. - Constant names must be UPPER_SNAKE_CASE. Example:
const MaxRetries = 3,const DefaultTimeout = 30 * time.Second.
- All Go files must be in the
cmd/directory for executables orinternal/for libraries. Never put code in root directory exceptmain.go. - Organize
internal/as:internal/models/,internal/handlers/,internal/services/,internal/storage/,internal/config/. - Test files must be in the same package and directory as the code they test. File name must be
*_test.go. Example:user_service.gopaired withuser_service_test.go. - All tests must use the standard
testingpackage. Use*testing.Treceiver. Never use external test frameworks. - Benchmark files must be named
*_bench_test.goand use*testing.Breceiver.
- Every exported function must have at least one test. Test function names must be
TestFunctionNameorTestFunctionName_Scenario. Example:TestGetUser,TestGetUser_NotFound. - Table-driven tests are mandatory for functions with multiple code paths. Use
[]struct { name string; input T; expected T; wantErr bool }. - Mock interfaces must be defined in
*_mock.gofiles in the same package. Usegithub.com/golang/mock/gomockfor code generation. - All tests must clean up resources in
deferstatements. Example:defer db.Close(),defer server.Close(). - Tests must not make real network calls or database queries. Use mocks or in-memory implementations.
- All HTTP handlers must have signature
func (h *Handler) MethodName(w http.ResponseWriter, r *http.Request). - Request body parsing must use
json.NewDecoder(r.Body).Decode(&v)and check for errors. Never useioutil.ReadAll()then unmarshal. - All JSON responses must be written with
json.NewEncoder(w).Encode(v)after settingw.Header().Set("Content-Type", "application/json"). - HTTP status codes must be explicit. Never return 200 by default. Use
http.StatusOK,http.StatusCreated,http.StatusBadRequest, etc. - Request validation must happen at the handler boundary. Validate input types, required fields, and ranges before passing to service layer.
- All configuration must be loaded from environment variables or a config file in
internal/config/. Never hardcode configuration values. - Secrets (API keys, database passwords, tokens) must be loaded from environment variables only. Never commit
.envfiles or hardcoded strings. - Use
os.Getenv()to read environment variables. For required vars, check if empty and return error:if key := os.Getenv("API_KEY"); key == "" { return fmt.Errorf("API_KEY not set") }. - Configuration structs must be defined in
internal/config/config.gowith struct tags for environment variable names.
- All database connections must be created once at startup and reused. Store in a
*sql.DBor connection pool, never create per-request. - All database queries must use parameterized queries with
?placeholders. Never concatenate strings into SQL. - Database operations must have explicit timeout contexts. Example:
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second); defer cancel(); row := db.QueryRowContext(ctx, ...). - All database rows must be closed with
defer rows.Close()immediately afterQuery()orQueryRow(). - Transaction handling must use explicit
Begin(),Commit(),Rollback(). Wrap in a helper function that handles rollback on panic.
- Never use
interface{}except in specific cases like JSON marshaling. Use generics or concrete types instead. - Never use type assertions without checking:
v, ok := x.(Type); if !ok { return err }. Never use barex.(Type). - Input validation must happen at API boundaries. Use a validation package or write explicit checks for string length, numeric ranges, email format.
- Struct fields that are required must not be pointers. Use non-pointer types for required fields, pointers only for optional fields.
- Use
time.Timefor timestamps, neverstringorint64. Parse withtime.Parse()and validate timezone.
- Never use
globalvariables for mutable state. Use dependency injection or pass values through function parameters. - Never use
init()functions. Initialize state inmain()or constructor functions. - Never use
deferin loops without understanding the scope. Defer executes at function end, not loop iteration end. - Never use
time.Sleep()in production code. Use channels, context cancellation, or timers. - Never ignore the second return value of
rangeon maps. Maps have undefined iteration order; if order matters, sort first. - Never use
unsafepackage outside of specific performance-critical sections with explicit comments.
Source: Codelibrium — the marketplace for AI behaviour files. Browse multiple rulesets at codelibrium.com or install via CLI:
npx codelibrium-cli install <ruleset-name>