Skip to content

Latest commit

 

History

History
95 lines (72 loc) · 8.18 KB

File metadata and controls

95 lines (72 loc) · 8.18 KB

Error Handling

  • Every function that returns (T, error) must have the error checked immediately on the next line. Never use _ to ignore errors. Use if err != nil { return err } or if err != nil { return fmt.Errorf("context: %w", err) }.
  • All error returns must wrap context using fmt.Errorf("operation description: %w", err). Never return bare err.
  • Custom error types must be defined as structs with an Error() string method. Use errors.Is() and errors.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 use fmt.Println(), log.Println(), or fmt.Printf() for errors.

Logging

  • All logging must use the slog package. Import as "log/slog". Never import "log" or use fmt.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: Debug for development details, Info for business events, Warn for recoverable issues, Error for failures that need attention.
  • Never log sensitive data: passwords, tokens, API keys, PII. If logging user data, hash or truncate it.

Context and Concurrency

  • Every function that performs I/O, calls other services, or runs in a goroutine must accept ctx context.Context as 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.WaitGroup or channels. Never spawn fire-and-forget goroutines in HTTP handlers.
  • Shared mutable state must be protected by sync.Mutex or sync.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.

Naming and Code Style

  • Package names must be lowercase, single word, no underscores. Example: auth, models, handlers — not auth_service or AuthService.
  • Exported functions and types must use PascalCase. Unexported must use camelCase. Example: func (s *Service) GetUser() not func (s *Service) get_user().
  • Interface names must end with er. Example: Reader, Writer, Handler, Logger — not IReader or ReadInterface.
  • Variable names must be concise but clear. Single-letter variables only for loop counters (i, j) or receiver names (r, w for http.ResponseWriter). Never use u for user or d for data.
  • File names must be lowercase with underscores for word separation. Example: user_service.go, http_handler.go — not UserService.go or userservice.go.
  • Constant names must be UPPER_SNAKE_CASE. Example: const MaxRetries = 3, const DefaultTimeout = 30 * time.Second.

File Structure and Organization

  • All Go files must be in the cmd/ directory for executables or internal/ for libraries. Never put code in root directory except main.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.go paired with user_service_test.go.
  • All tests must use the standard testing package. Use *testing.T receiver. Never use external test frameworks.
  • Benchmark files must be named *_bench_test.go and use *testing.B receiver.

Testing Requirements

  • Every exported function must have at least one test. Test function names must be TestFunctionName or TestFunctionName_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.go files in the same package. Use github.com/golang/mock/gomock for code generation.
  • All tests must clean up resources in defer statements. Example: defer db.Close(), defer server.Close().
  • Tests must not make real network calls or database queries. Use mocks or in-memory implementations.

HTTP and API Handlers

  • 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 use ioutil.ReadAll() then unmarshal.
  • All JSON responses must be written with json.NewEncoder(w).Encode(v) after setting w.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.

Configuration and Secrets

  • 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 .env files 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.go with struct tags for environment variable names.

Database and Storage

  • All database connections must be created once at startup and reused. Store in a *sql.DB or 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 after Query() or QueryRow().
  • Transaction handling must use explicit Begin(), Commit(), Rollback(). Wrap in a helper function that handles rollback on panic.

Type Safety and Validation

  • 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 bare x.(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.Time for timestamps, never string or int64. Parse with time.Parse() and validate timezone.

Forbidden Patterns

  • Never use global variables for mutable state. Use dependency injection or pass values through function parameters.
  • Never use init() functions. Initialize state in main() or constructor functions.
  • Never use defer in 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 range on maps. Maps have undefined iteration order; if order matters, sort first.
  • Never use unsafe package 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>