Use when you have a written implementation plan to execute in a separate session with review checkpoints
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.mdfor examples. -
backend/cmd/: Application entry points. Keepmain.goslim; use it for configuration and component initialization. backend/internal/: Project-internal code.handler/: HTTP layer. UsesHandlergeneric wrapper. Defers logic toservice.service/: Domain logic layer. Orchestrates business rules and repository calls.repository/: Data layer. Handles raw SQL queries viapgxpool.models/: Data structures (DB models, DTOs, request/response types).
backend/pkg/: Shared utility packages (e.g., customstatemachine).backend/migrations/: SQL migration scripts.
2. Core Architectural Patterns
Layered Responsibility
- See
references/LAYERED_RESPONSIBILITY.mdfor 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.mdfor examples. - When request parameters require validation, implement a
Validatorinterface 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.mdfor examples.
Every HTTP endpoint should be wrapped using theHandler[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.mdfor 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.mdfor examples. - Pass
context.Contextto allserviceandrepositorymethods to support cancellation and timeouts.
Database: Raw SQL with pgx or sqlx
- See
references/DATABASE_ACCESS.mdfor examples. - Prefer raw SQL over ORMs for performance and transparency.
- Use
github.com/jackc/pgx/v5andpgxpool, orgithub.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.mdfor 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.mdfor 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.mdfor 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.Contextparameter. This allows the use ofcontext.Contextto 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.mdfor 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.mdfor 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.mdfor 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.ymldoes 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.mdfor 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
jsontags.
Must-Prefix Functions & Panic Safety
- See
references/MUST_FUNCTIONS.mdfor examples. - Must-prefix functions (e.g.,
template.Must,regexp.MustCompile) and helper functions likefunc Must[T any](t T, err error) Tthat 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.mdfor 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.mdfor 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:
- Use constructor-based dependency injection
- Pass
context.Contextas the first parameter - Use raw SQL with pgx/sqlx instead of ORMs
- Return concrete types, accept interfaces
- Use
uuid.UUIDfor identifiers - Define typed errors as package variables
- Add proper
jsontags to all models - Write tests with mockery-generated mocks
- Use functional options or request structs for extensibility
- Avoid parameter mutation (unless strictly required for performance)
- Apply middleware for cross-cutting concerns
- Keep handlers thin - defer to services
- Use event-driven or hook patterns for extensibility
- Use goroutine pools for high-concurrency dynamic tasks
- Limit Must-functions to main.go initialization or guaranteed-safe inputs
- Ensure infinite loop goroutines are cancellable via context
- Handle all errors explicitly; do not ignore them
- Close channels from the writer side
- Use composable Validator interface for request validation
❌ Never Do:
- Put business logic in handlers or main.go
- Use global variables for dependencies
- Forget context in service/repository methods
- Use ORMs instead of raw SQL
- Use string IDs instead of UUIDs
- Return generic string errors
- Expose sensitive fields (like passwords) in JSON
- Write untestable code with tight coupling
- Add too many positional parameters
- Mutate input parameters (pointers) within functions without performance justification
- Hardcode side effects in core business logic
- Over-engineer simple solutions
- Add unnecessary external dependencies
- Create unbounded goroutines in hot paths
- Use Must-prefix functions or panic helpers with user input or in request paths
- Run infinite loop goroutines without a cancellation mechanism
- Ignore errors or use
_to suppress them - 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
Handlerwrapper. - Simplicity (KISS): Prioritize readable, simple code over complex abstractions.
Documentation Map
The references/ directory contains detailed guides on specific topics.
🏗 Architecture & Structure
- Project Organization: Standard directory layout (
cmd,internal,pkg). - Core Architectural Patterns: High-level overview of the system design.
- Layered Responsibility: Rules for what goes into Handlers vs. Services vs. Repositories.
- Dependency Injection: How to manage dependencies using constructors.
🔌 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
- Database Access: Patterns for using raw SQL with
pgxorsqlx. - Error Handling: Typed errors and return value conventions.
- Extensibility: Designing for future growth without breaking changes.
🎨 Coding Standards
- Design Principles: SOLID, KISS, and other guiding principles.
- Standard Library First: Guidelines on avoiding unnecessary libraries.
- Function Parameter Design: Best practices for function signatures and options patterns.
- Testing: Strategies for unit and integration testing.
⚠️ Error & Panic Safety
- Constructor Error Handling: How constructors should handle errors instead of panicking.
- Must-Prefix Functions: Safe usage patterns for Must-functions and panic helpers.
🔄 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.