septemhill

Golang Backend Development

0
0
# Install this skill:
npx skills add septemhill/go-backend-dev-skill

Or install specific skill: npx add-skill https://github.com/septemhill/go-backend-dev-skill

# Description

Architectural standards and coding practices for the Go backend.

# SKILL.md


name: Golang Backend Development
description: Architectural standards and coding practices for the Go backend.


Golang Backend Development Standards

This skill defines the architectural requirements, coding standards, and best practices for the Golang backend. AI agents must adhere to these guidelines to ensure consistency, maintainability, and extensibility.

1. Project Organization

  • See references/PROJECT_ORGANIZATION.md for examples.

  • backend/cmd/: Application entry points. Keep main.go slim; use it for configuration and component initialization.

  • backend/internal/: Project-internal code.
    • handler/: HTTP layer. Uses Handler generic wrapper. Defers logic to service.
    • service/: Domain logic layer. Orchestrates business rules and repository calls.
    • repository/: Data layer. Handles raw SQL queries via pgxpool.
    • models/: Data structures (DB models, DTOs, request/response types).
  • backend/pkg/: Shared utility packages (e.g., custom statemachine).
  • backend/migrations/: SQL migration scripts.

2. Core Architectural Patterns

Layered Responsibility

  • See references/LAYERED_RESPONSIBILITY.md for examples.
  • Handlers must NOT contain business logic. They decode requests, call services, and encode responses.
  • Services are the source of truth for business rules. They must be agnostic to the delivery method (HTTP).
  • Repositories bridge the domain models and the database.

Interface Design

  • Keep interfaces small with minimal methods to provide maximum flexibility for implementers.
  • Return Structs, Accept Interfaces: Functions should return concrete types even if they implement an interface. Parameters should be interfaces to reduce coupling.

Request Validation

  • See references/VALIDATOR_INTERFACE.md for examples.
  • When request parameters require validation, implement a Validator interface pattern.
  • Use a composable approach (e.g., AllPassValidator) to combine multiple validation rules.
  • Inject the validator into the service to keep validation logic decoupled from business logic.

The Handler Wrapper

  • See references/THE_HANDLER_WRAPPER.md for examples.
    Every HTTP endpoint should be wrapped using the Handler[REQ, RESP]. This ensures consistent:
  • Request decoding (DecodeFunc, DefaultDecoder)
  • Pre-handling: Logic executed before the main handler (e.g., validation)
  • Post-handling: Logic executed after the main handler (e.g., logging)
  • Response encoding (EncodeFunc, DefaultEncoder)
  • Error handling (DefaultErrorHandler)

Dependency Injection

Dependencies must be injected via constructors:
- See references/DEPENDENCY_INJECTION.md for examples.

3. Coding Standards & Tooling

Standard Library First

  • See references/STANDARD_LIBRARY_FIRST.md for examples.
  • Always prioritize using the Go standard library to implement functionality unless strictly necessary. Keep external dependencies to a minimum.

Context First

  • See references/CONTEXT_USAGE.md for examples.
  • Pass context.Context to all service and repository methods to support cancellation and timeouts.

Database: Raw SQL with pgx or sqlx

  • See references/DATABASE_ACCESS.md for examples.
  • Prefer raw SQL over ORMs for performance and transparency.
  • Use github.com/jackc/pgx/v5 and pgxpool, or github.com/jmoiron/sqlx.
  • Scan results into structs defined in internal/models.

HTTP Middleware

Apply cross-cutting concerns via middleware:
- See references/MIDDLEWARE.md for examples.
- LoggingMiddleware: Records full request/response details for debugging.
- AuthMiddleware: Validates JWT and extracts userID.
- CORSMiddleware: Standard CORS headers.

Function Parameter Design

  • See references/FUNCTION_PARAMETER_DESIGN.md for examples.
  • Backward Compatibility: Design function parameters with backward compatibility in mind. Use patterns like Functional Options or wrap multiple parameters into a Request/Options Struct to avoid breaking changes when extending functionality.
  • Avoid Parameter Mutation:
  • See references/AVOID_MUTATING_PARAMETERS.md for examples.
  • Avoid modifying input parameters within a function, even if they are pointer types. This reduces side effects and makes call-site behavior more predictable.
  • Exception: Mutation is only permissible when justified by severe performance requirements (e.g., high-frequency hot paths, extremely large structs).

Concurrency & Goroutines

  • See references/GOROUTINE_POOLS.md for examples.
  • If dynamic generation of a large number of goroutines is required (e.g., in a heavily called handler), you must use a goroutine pool for management.
  • Unbounded goroutine creation can lead to memory leaks. See issue #9869.
  • When starting a new task using a goroutine, if the task involves an infinite loop, ensure that the goroutine accepts a context.Context parameter. This allows the use of context.Context to terminate the loop, avoiding memory leaks.
  • Channel Ownership: The goroutine that writes to a channel should be the one responsible for closing it. This prevents panic scenarios where a channel is closed while being written to.
  • Mutex Locking:
  • See references/MUTEX_LOCKING.md for examples.
  • Minimize the duration mutex locks are held.
  • When multiple independent resources require locking, use fine-grained locking (one mutex per resource) to avoid unnecessary blocking. Use the generic Mutex[T] pattern to enforce this.

Design Principles

  • See references/DESIGN_PRINCIPLES.md for examples.
  • KISS (Keep It Simple and Stupid): Prioritize simplicity over complexity. Avoid over-engineering.
  • SOLID: Follow SOLID principles to ensure maintainable and scalable code, but always prioritize KISS:
    • S - Single Responsibility Principle (SRP): A class or function should have one, and only one, reason to change.
    • O - Open/Closed Principle (OCP): Entities should be open for extension, but closed for modification.
    • L - Liskov Substitution Principle (LSP): Derived types must be completely substitutable for their base types.
    • I - Interface Segregation Principle (ISP): Clients should not be forced to depend on interfaces they do not use.
    • D - Dependency Inversion Principle (DIP): Depend on abstractions, not on concretions.

Testing & Coverage

  • See references/TESTING.md for examples.
  • All service, repository, and package implementations must have test case coverage.
  • Mocking: Use the mockery library to generate mock files for interfaces. This facilitates consistent and easy unit testing.
  • Prioritize testability in all implementations.

4. Code Quality & Linting

  • Configuration: If .golangci.yml does not exist in the project root, create one using the reference configuration from golangci-lint reference.
  • Enforcement: Run golangci-lint run ./... on the entire project after making changes or fixing issues to ensure code quality and consistency.

4. Error Handling & Return Values

  • See references/ERROR_HANDLING.md for examples.
  • Handle All Errors: Ensure all errors are properly handled. Ignoring errors or only checking them without action can lead to silent failures.
  • Return clear, typed errors from services (e.g., ErrUnauthorized).
  • Ensure all JSON-serialized fields have appropriate json tags.

Must-Prefix Functions & Panic Safety

  • See references/MUST_FUNCTIONS.md for examples.
  • Must-prefix functions (e.g., template.Must, regexp.MustCompile) and helper functions like func Must[T any](t T, err error) T that panic on error must only be used in safe contexts:
  • Initialization in main.go: Where immediate failure is acceptable and expected (e.g., loading critical configuration, compiling static regexes)
  • Guaranteed safe inputs: Where you can manually verify that the input will never cause an error (e.g., hardcoded valid regex patterns, compile-time constants)
  • Never use Must-functions with:
  • User input or external data
  • Runtime-generated values that could be invalid
  • Any operation in request handlers, services, or repositories where recovery is possible
  • Rationale: Panics in production services cause crashes and service disruption. Proper error handling allows graceful degradation and better observability.

Constructor Error Handling

  • See references/CONSTRUCTOR_ERROR_HANDLING.md for examples.
  • If a constructor executes logic that returns an error (e.g., parsing config, opening DB), the constructor must return (*Type, error) instead of panicking.

5. Extensibility Goal

  • See references/EXTENSIBILITY.md for examples.
  • When implementing new features, consider how side effects (notifications, logs, state transitions) can be hooked into the existing flow without tightly coupling the core business logic.

Summary of Key Patterns

✅ Always Do:

  1. Use constructor-based dependency injection
  2. Pass context.Context as the first parameter
  3. Use raw SQL with pgx/sqlx instead of ORMs
  4. Return concrete types, accept interfaces
  5. Use uuid.UUID for identifiers
  6. Define typed errors as package variables
  7. Add proper json tags to all models
  8. Write tests with mockery-generated mocks
  9. Use functional options or request structs for extensibility
  10. Avoid parameter mutation (unless strictly required for performance)
  11. Apply middleware for cross-cutting concerns
  12. Keep handlers thin - defer to services
  13. Use event-driven or hook patterns for extensibility
  14. Use goroutine pools for high-concurrency dynamic tasks
  15. Limit Must-functions to main.go initialization or guaranteed-safe inputs
  16. Ensure infinite loop goroutines are cancellable via context
  17. Handle all errors explicitly; do not ignore them
  18. Close channels from the writer side
  19. Use composable Validator interface for request validation

❌ Never Do:

  1. Put business logic in handlers or main.go
  2. Use global variables for dependencies
  3. Forget context in service/repository methods
  4. Use ORMs instead of raw SQL
  5. Use string IDs instead of UUIDs
  6. Return generic string errors
  7. Expose sensitive fields (like passwords) in JSON
  8. Write untestable code with tight coupling
  9. Add too many positional parameters
  10. Mutate input parameters (pointers) within functions without performance justification
  11. Hardcode side effects in core business logic
  12. Over-engineer simple solutions
  13. Add unnecessary external dependencies
  14. Create unbounded goroutines in hot paths
  15. Use Must-prefix functions or panic helpers with user input or in request paths
  16. Run infinite loop goroutines without a cancellation mechanism
  17. Ignore errors or use _ to suppress them
  18. Close channels from the receiver side

Remember: Follow KISS principle first, then apply SOLID where it adds clear value. Prioritize testability, maintainability, and extensibility in all implementations.

# README.md

Golang Backend Standards & Skills

This repository defines the architectural standards, coding patterns, and best practices for building scalable, maintainable, and idiomatic Go backend services. It serves as the source of truth for both human developers and AI agents working on the project.

Core Philosophy

  • Standard Library First: Minimize external dependencies. Use the robust Go standard library whenever possible.
  • Layered Architecture: Strict separation of concerns between Transports (HTTP), Business Logic (Service), and Data Access (Repository).
  • Type Safety: Leverage Go's type system to enforce contracts, especially at the API boundaries using the Handler wrapper.
  • Simplicity (KISS): Prioritize readable, simple code over complex abstractions.

Documentation Map

The references/ directory contains detailed guides on specific topics.

🏗 Architecture & Structure

🔌 HTTP & API

  • The Handler Wrapper: The generic Handler[REQ, RESP] pattern for standardized API endpoints.
  • Middleware: Handling cross-cutting concerns like auth and logging.
  • Request Validation: Patterns for validating request parameters using composable interfaces.
  • Context Usage: Proper propagation of context.Context.

💾 Data & Logic

🎨 Coding Standards

⚠️ Error & Panic Safety

🔄 Concurrency

  • Goroutine Pools: Managing goroutines to avoid memory leaks and unbounded concurrency.
  • Mutex Locking: Best practices for minimizing lock contention using fine-grained locking.

For AI Agents

  • SKILL.md: This is the primary instruction file for AI agents. It summarizes the critical rules from the references above to ensure the AI generates code that adheres to these standards.

# Supported AI Coding Agents

This skill is compatible with the SKILL.md standard and works with all major AI coding agents:

Learn more about the SKILL.md standard and how to use these skills with your preferred AI coding agent.