Clean Code in Go: Principles and Practical Examples

Online Python Trainer for Beginners

Learn Python easily without overwhelming theory. Solve practical tasks with automatic checking, get hints in Russian, and write code directly in your browser — no installation required.

Start Course

Clean Code in Go: Principles and Practical Examples

\n\n

Go is a language that was designed from the start with an emphasis on simplicity and readability. However, even in such a concise language, you can write confusing and hard-to-maintain code. The concept of Clean Code helps developers create programs that are easy to read, test, and modify. In this article, we will break down the key principles of Clean Code as applied to Go and look at specific examples.

\n\n

Why is clean code important in Go?

\n\n

Go is often used to create microservices, APIs, and high-load systems. In such projects, a team of developers works on the code. Clean code reduces cognitive load, speeds up code reviews, and reduces the number of bugs. The main goal is to write code so that it is understandable not only to the author but also to any other developer who sees it for the first time.

\n\n

1. Naming Variables and Functions

\n\n

Proper naming is the foundation of clean code. In Go, it is common to use short names for local variables and longer names for global entities.

\n\n

Bad Example

\n
package main\n\nfunc f(x int, y string) int {\n    z, _ := strconv.Atoi(y)\n    return x + z\n}\n
\n\n

Good Example

\n
package main\n\nfunc addPriceToCount(count int, priceStr string) int {\n    price, _ := strconv.Atoi(priceStr)\n    return count + price\n}\n
\n\n

Naming rules in Go:

\n
    \n
  • Use camelCase for unexported (private) variables.
  • \n
  • Use PascalCase for exported (public) functions and types.
  • \n
  • Avoid single-letter names, except for loop indices (i, j).
  • \n
  • Do not include the variable type in its name (e.g., priceInt is bad, price is good).
  • \n
\n\n

2. Package and File Structure

\n\n

In Go, each package should solve one task. Separate code by responsibility. A standard project structure looks like this:

\n\n
myproject/\n├── cmd/\n│   └── server/\n│       └── main.go\n├── internal/\n│   ├── handler/\n│   ├── service/\n│   └── repository/\n├── pkg/\n│   └── utils/\n└── go.mod\n
\n\n

Principle: The internal package protects code from external imports. The pkg package contains reusable utilities. The cmd directory stores entry points (main.go files).

\n\n

Bad Example

\n
// The "utils" package contains everything\npackage utils\n\nfunc Calculate(x, y int) int { ... }\nfunc SendEmail(to string) { ... }\nfunc ParseJSON(data []byte) { ... }\n
\n\n

Good Example

\n
// The "calculator" package is only responsible for calculations\npackage calculator\n\nfunc Add(a, b int) int { return a + b }\n
\n\n

3. Error Handling in Go

\n\n

Go has no exceptions — errors are returned as values. Clean code requires explicit and meaningful handling of each error.

\n\n

Bad Example

\n
func readFile(path string) string {\n    data, _ := os.ReadFile(path) // ignoring the error\n    return string(data)\n}\n
\n\n

Good Example

\n
func readFile(path string) (string, error) {\n    data, err := os.ReadFile(path)\n    if err != nil {\n        return "", fmt.Errorf("read file %s: %w", path, err)\n    }\n    return string(data), nil\n}\n
\n\n

Tips for error handling:

\n
    \n
  • Never ignore errors using _.
  • \n
  • Always add context: fmt.Errorf("operation: %w", err).
  • \n
  • Use custom error types for different situations.
  • \n
  • Do not use panic for ordinary errors — only for truly exceptional situations.
  • \n
\n\n

4. Functions: Length and Responsibility

\n\n

A function

Blogs

Book Recommendations