Claude-skill-registry Golang Backend Development
Architectural standards and coding practices for the Go backend.
install
source · Clone the upstream repo
git clone https://github.com/majiayu000/claude-skill-registry
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/majiayu000/claude-skill-registry "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/data/go-backend-dev-skill" ~/.claude/skills/majiayu000-claude-skill-registry-golang-backend-development && rm -rf "$T"
manifest:
skills/data/go-backend-dev-skill/SKILL.mdsource content
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
for examples.references/PROJECT_ORGANIZATION.md -
: Application entry points. Keepbackend/cmd/
slim; use it for configuration and component initialization.main.go -
: Project-internal code.backend/internal/
: HTTP layer. Useshandler/
generic wrapper. Defers logic toHandler
.service
: Domain logic layer. Orchestrates business rules and repository calls.service/
: Data layer. Handles raw SQL queries viarepository/
.pgxpool
: Data structures (DB models, DTOs, request/response types).models/
-
: Shared utility packages (e.g., custombackend/pkg/
).statemachine -
: SQL migration scripts.backend/migrations/
2. Core Architectural Patterns
Layered Responsibility
- See
for examples.references/LAYERED_RESPONSIBILITY.md - 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
for examples.references/VALIDATOR_INTERFACE.md - When request parameters require validation, implement a
interface pattern.Validator - Use a composable approach (e.g.,
) to combine multiple validation rules.AllPassValidator - Inject the validator into the service to keep validation logic decoupled from business logic.
The Handler
Wrapper
Handler- See
for examples. Every HTTP endpoint should be wrapped using thereferences/THE_HANDLER_WRAPPER.md
. This ensures consistent:Handler[REQ, RESP] - 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
for examples.references/DEPENDENCY_INJECTION.md
3. Coding Standards & Tooling
Standard Library First
- See
for examples.references/STANDARD_LIBRARY_FIRST.md - Always prioritize using the Go standard library to implement functionality unless strictly necessary. Keep external dependencies to a minimum.
Context First
- See
for examples.references/CONTEXT_USAGE.md - Pass
to allcontext.Context
andservice
methods to support cancellation and timeouts.repository
Database: Raw SQL with pgx
or sqlx
pgxsqlx- See
for examples.references/DATABASE_ACCESS.md - Prefer raw SQL over ORMs for performance and transparency.
- Use
andgithub.com/jackc/pgx/v5
, orpgxpool
.github.com/jmoiron/sqlx - Scan results into structs defined in
.internal/models
HTTP Middleware
Apply cross-cutting concerns via middleware:
- See
for examples.references/MIDDLEWARE.md
: Records full request/response details for debugging.LoggingMiddleware
: Validates JWT and extractsAuthMiddleware
.userID
: Standard CORS headers.CORSMiddleware
Function Parameter Design
- See
for examples.references/FUNCTION_PARAMETER_DESIGN.md - 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
for examples.references/AVOID_MUTATING_PARAMETERS.md - 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).
- See
Concurrency & Goroutines
- See
for examples.references/GOROUTINE_POOLS.md - 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
parameter. This allows the use ofcontext.Context
to terminate the loop, avoiding memory leaks.context.Context - 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
for examples.references/MUTEX_LOCKING.md - 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
pattern to enforce this.Mutex[T]
- See
Design Principles
- See
for examples.references/DESIGN_PRINCIPLES.md - 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
for examples.references/TESTING.md - 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
does not exist in the project root, create one using the reference configuration from golangci-lint reference..golangci.yml - Enforcement: Run
on the entire project after making changes or fixing issues to ensure code quality and consistency.golangci-lint run ./...
4. Error Handling & Return Values
- See
for examples.references/ERROR_HANDLING.md - 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
tags.json
Must-Prefix Functions & Panic Safety
- See
for examples.references/MUST_FUNCTIONS.md - Must-prefix functions (e.g.,
,template.Must
) and helper functions likeregexp.MustCompile
that panic on error must only be used in safe contexts:func Must[T any](t T, err error) T- Initialization in
: Where immediate failure is acceptable and expected (e.g., loading critical configuration, compiling static regexes)main.go - 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)
- Initialization in
- 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
for examples.references/CONSTRUCTOR_ERROR_HANDLING.md - If a constructor executes logic that returns an error (e.g., parsing config, opening DB), the constructor must return
instead of panicking.(*Type, error)
5. Extensibility Goal
- See
for examples.references/EXTENSIBILITY.md - 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.
6. Performance Optimization
- See
for examples.references/PERFORMANCE_OPTIMIZATION.md - Object Pooling: For objects that are frequently allocated and deallocated, use
to reduce GC pressure.sync.Pool - Stack Allocation: Prefer returning concrete objects (values) rather than pointers when the object is small or where stack allocation is possible, reducing the burden on the heap and GC.
- Hot Paths: Only apply aggressive optimizations in verified hot paths where performance issues are significant.
Summary of Key Patterns
✅ Always Do:
- Use constructor-based dependency injection
- Pass
as the first parametercontext.Context - Use raw SQL with pgx/sqlx instead of ORMs
- Return concrete types, accept interfaces
- Use
for identifiersuuid.UUID - Define typed errors as package variables
- Add proper
tags to all modelsjson - 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.