User Tools

Site Tools


go:architecture:coding_convention

This is an old revision of the document!


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:

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
go/architecture/coding_convention.1769737781.txt.gz · Last modified: by phong2018