Table of Contents

GOLANG CODING CONVENTION

This document defines the coding conventions for projects written in Go (Golang).

Goals

These conventions apply to all Go code in this repository unless explicitly stated otherwise.

Table of Contents

1. General Principles

<anchor general_principles />

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

Examples:

File naming examples:

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

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:

5. Functions and Methods

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:

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:

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

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