User Tools

Site Tools


go:architecture:coding_convention

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

Identifiers starting with an uppercase letter are exported. Identifiers starting with a lowercase letter are unexported.

2.1 Package Names

  • Use lowercase names
  • Do not use CamelCase
  • Avoid underscores unless absolutely necessary

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

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

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

Constants typically use PascalCase.

const (
    DefaultTimeout = 30 * time.Second
    HTTPTimeout    = 10 * time.Second
    MaxRetries     = 3
)

Common acronyms should be all caps.

type HTTPServer struct{}
type UserID string
 
func NewJSONEncoder() JSONEncoder {}
 
const HTTPTimeout = 10 * time.Second

2.5 General Naming Guidelines

; Types : Should be nouns or noun phrases : Examples: ``User``, ``Order``, ``UserService``

; Functions and methods : Should be verbs or verb phrases : Examples: ``CreateUser``, ``ValidateToken``

; Interfaces : Should, where appropriate, end with ``-er`` : Examples: ``Reader``, ``Writer``, ``UserRepository``

; Short-lived or small scopes : Used in loops or short blocks : Examples: ``i``, ``j``, ``n``, ``err``

; Longer-lived or wider scopes : Should use meaningful names : Examples: ``userID``, ``ctx``, ``cfg``

3. Formatting

  • Always format code using ``gofmt`` or ``goimports``
  • Tabs are used for indentation
  • Opening braces must be on the same line
if condition {
    // ...
}

Import grouping example:

import (
    "context"
    "fmt"
    "net/http"
 
    "github.com/yourorg/yourproject/internal/user"
)

4. Project Structure

.
├── cmd/
│   └── appname/
│       └── main.go
├── internal/
│   ├── domain/
│   ├── infrastructure/
│   ├── presentation/
│   ├── config/
│   ├── constant/
│   ├── validator/
├── pkg/
├── configs/
└── go.mod

Guidelines:

  • ``cmd``: application entry points and wiring
  • ``internal``: non-public application code
  • ``pkg``: reusable libraries (if needed)

5. Functions and Methods

  • Functions should have a single responsibility
  • Avoid very long functions (over ~100 lines)
  • Prefer no more than 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

Always handle errors immediately.

user, err := s.repo.FindByID(ctx, id)
if err != nil {
    return nil, err
}

Use ``errors.Is`` and ``errors.As``:

if errors.Is(err, sql.ErrNoRows) {
    return nil, ErrUserNotFound
}

Error message rules:

  • Start with a lowercase letter
  • Do not end with a period
  • Be short and descriptive
return fmt.Errorf("failed to save user: %w", err)

7. 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 comments that repeat code behavior.

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

8.1 Configuration

Centralize configuration loading and validation in ``internal/config``.

8.2 context.Context

Rules:

  • Must be the first parameter
  • Must not be stored in struct fields
  • Must never be nil
func (s *UserService) GetByID(
    ctx context.Context,
    id string,
) (*User, error) {
    // ...
}

8.3 Logging

Use ``log/slog`` for structured logging.

slog.InfoContext(ctx, "user created", "user_id", userID)
slog.ErrorContext(ctx, "failed to create user", "error", err)

9. Batch Coding

Batch jobs must 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

  • Use the ``testing`` package
  • Prefer table-driven tests
  • Use ``testify/assert`` for assertions
  • Use ``gomock`` for mocking
  • Suppress logs in tests

Dependency injection example:

func NewPointAddUsecase(
    repo IAddPointRepository,
    crm IYappliCRMClient,
) *PointAddUsecase {
    return &PointAddUsecase{
        repo: repo,
        crm:  crm,
    }
}

11. Tools and Static Analysis

11.1 gofmt

gofmt -w ./...

11.2 goimports

Formats code and manages imports automatically.

11.3 go vet

go vet ./...

11.4 golangci-lint

Configured via ``.golangci.yml``.

linters:
  enable:
    - govet
    - staticcheck
    - errcheck
go/architecture/coding_convention.txt · Last modified: by phong2018