This is an old revision of the document!
Table of Contents
GOLANG CODING CONVENTION
This document defines the coding conventions for projects written in Go (Golang).
Goals
- Improve readability and maintainability of the codebase
- Ensure a consistent coding style across the team
- Reduce friction in code reviews and onboarding
- Leverage Go’s ecosystem and tools effectively
These conventions apply to all Go code in this repository unless explicitly stated otherwise.
Table of Contents
1. General Principles
<anchor general_principles />
- Prefer clarity over cleverness
- Keep functions, types, and packages small and focused
- Follow idiomatic Go practices
References:
- Go standard library (fmt, net/http, context, time, strings, errors, io, etc.)
When in doubt, idiomatic Go style takes precedence over personal preference.
2. Naming Conventions
<anchor naming_conventions />
Identifiers starting with an uppercase letter are exported. Identifiers starting with a lowercase letter are unexported.
2.1 Package Names
<anchor package_names />
- Use lowercase
- No CamelCase
- Avoid underscores
Examples:
- ``package user``
- ``package auth``
- ``package config``
File naming examples:
- ``user_service.go``
- ``user_repository.go``
- ``http_handler.go``
2.2 Variables, Functions, and Methods
<anchor variables_functions_and_methods />
Use camelCase for unexported identifiers.
var maxRetries = 3 func newUserRepo(db *sql.DB) *userRepo { return &userRepo{db: db} } type userRepo struct { db *sql.DB }
2.3 Structs, Interfaces, and Exported Symbols
<anchor structs_interfaces_and_exported_symbols />
Use PascalCase for exported identifiers.
type UserService struct { Repo UserRepository } func NewUserService(repo UserRepository) *UserService { return &UserService{Repo: repo} } type UserRepository interface { FindByID(ctx context.Context, id string) (*User, error) }
2.4 Constants and Acronyms
<anchor constants_and_acronyms />
Constants typically use PascalCase.
const ( DefaultTimeout = 30 * time.Second HTTPTimeout = 10 * time.Second MaxRetries = 3 )
Acronyms should be all caps:
type HTTPServer struct{} type UserID string func NewJSONEncoder() JSONEncoder {} const HTTPTimeout = 10 * time.Second
2.5 General Naming Guidelines
<anchor general_naming_guidelines />
- Types: nouns → ``User``, ``Order``, ``UserService``
- Functions: verbs → ``CreateUser``, ``ValidateToken``
- Interfaces: often end with ``-er`` → ``Reader``, ``UserRepository``
- Short scopes: ``i``, ``j``, ``err`` are OK
- Long scopes: use meaningful names → ``userID``, ``ctx``, ``cfg``
3. Formatting
<anchor formatting />
- Always use ``gofmt`` or ``goimports``
- Tabs for indentation
- Opening brace on the same line
if condition { // ... }
Import grouping example:
import ( "context" "fmt" "net/http" "github.com/yourorg/yourproject/internal/user" )
4. Project Structure
<anchor project_structure />
. ├── cmd/ │ └── appname/ │ └── main.go ├── internal/ │ ├── domain/ │ ├── infrastructure/ │ ├── presentation/ │ ├── config/ │ ├── constant/ │ ├── validator/ ├── pkg/ ├── configs/ └── go.mod
Guidelines:
- ``cmd``: application entry points
- ``internal``: non-public application code
- ``pkg``: reusable libraries
5. Functions and Methods
<anchor functions_and_methods />
- Single responsibility
- Avoid >100 lines
- Prefer ≤3 parameters
Options struct example:
type CreateUserOptions struct { Name string Email string Role string } func (s *UserService) CreateUser(ctx context.Context, opts CreateUserOptions) (*User, error) { // ... }
Prefer early returns:
if err != nil { return nil, err }
6. Error Handling
<anchor error_handling />
Always handle errors immediately.
user, err := s.repo.FindByID(ctx, id) if err != nil { return nil, err }
Use ``errors.Is`` / ``errors.As``:
if errors.Is(err, sql.ErrNoRows) { return nil, ErrUserNotFound }
Error message rules:
- lowercase
- no period
- descriptive
return fmt.Errorf("failed to save user: %w", err)
7. Comments and Documentation
<anchor comments_and_documentation />
7.1 Doc Comments
Every exported identifier must have a doc comment.
// UserService handles user-related operations. type UserService struct {}
7.2 Comment the Why
Avoid repeating code logic.
Bad:
// Increase increases the counter by 1. func (c *Counter) Increase() { c.value++ }
Good:
// Increase increments the counter safely for concurrent use. func (c *Counter) Increase() {}
8. Configuration, Context and Logging
<anchor configuration_context_and_logging />
8.1 Configuration
Centralize configuration in ``internal/config``.
8.2 context.Context
Rules:
- First parameter
- Never stored in structs
- Never nil
func (s *UserService) GetByID(ctx context.Context, id string) (*User, error) {}
8.3 Logging
Use ``log/slog``.
slog.InfoContext(ctx, "user created", "user_id", userID) slog.ErrorContext(ctx, "failed", "error", err)
9. Batch Coding
<anchor batch_coding />
Batch jobs implement:
Run(ctx context.Context, args []string) error
Healthcheck example:
type healthcheck struct{} func (h *healthcheck) Run(ctx context.Context, args []string) error { slog.InfoContext(ctx, "OK") return nil }
10. Testing
<anchor testing />
- Use ``testing`` package
- Table-driven tests
- ``testify/assert``
- ``gomock`` for mocks
- Suppress logs in tests
Dependency injection example:
func NewPointAddUsecase(repo IAddPointRepository, crm IYappliCRMClient) *PointAddUsecase {}
11. Tools and Static Analysis
<anchor tools_and_static_analysis />
11.1 gofmt
gofmt -w ./...
11.2 goimports
Formats code and manages imports.
11.3 go vet
go vet ./...
11.4 golangci-lint
Configured via ``.golangci.yml``.
linters: enable: - govet - staticcheck - errcheck
