====== 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 =====
* [[#general_principles|1. General Principles]]
* [[#naming_conventions|2. Naming Conventions]]
* [[#package_names|2.1 Package Names]]
* [[#variables_functions_and_methods|2.2 Variables, Functions, and Methods]]
* [[#structs_interfaces_and_exported_symbols|2.3 Structs, Interfaces, and Exported Symbols]]
* [[#constants_and_acronyms|2.4 Constants and Acronyms]]
* [[#general_naming_guidelines|2.5 General Naming Guidelines]]
* [[#formatting|3. Formatting]]
* [[#project_structure|4. Project Structure]]
* [[#functions_and_methods|5. Functions and Methods]]
* [[#error_handling|6. Error Handling]]
* [[#comments_and_documentation|7. Comments and Documentation]]
* [[#configuration_context_and_logging|8. Configuration, Context, and Logging]]
* [[#batch_coding|9. Batch Coding]]
* [[#testing|10. Testing]]
* [[#tools_and_static_analysis|11. Tools and Static Analysis]]
===== 1. General Principles =====
* Prefer clarity over cleverness
* Keep functions, types, and packages small and focused
* Follow idiomatic Go practices
References:
* [[https://golang.org/doc/effective_go.html|Effective Go]]
* [[https://go.dev/wiki/CodeReviewComments|Go Code Review Comments]]
* 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 =====
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