How to Write Clean Code in Go: 7 Rules for Pros

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

Introduction: Why Clean Code in Go is Not a Luxury, but a Necessity

\n\n

Go (or Golang) is a language that at first glance seems simple and even boring. It has no generics (until version 1.18), no classes, no virtual machine. But it is precisely this minimalism that makes it an ideal battleground for clean code. If you write in Go and your project starts to resemble spaghetti of goroutines and unreadable switch statements, you are losing the language's main advantage: development speed and ease of maintenance.

\n\n

In this article, we will break down 7 practical rules that will help you write idiomatic Go code that will be understandable not only to the compiler but also to your colleagues (and to yourself in a month). Let's go!

\n\n

1. Proper Variable Names: Short and Clear

\n\n

In Go, it is customary to use short but meaningful names. Unlike Java, where ListOfUserOrders is the norm, in Go you will see orders or even os. Why? Because Go is a language for reading code, not for writing it.

\n\n

Basic Naming Rules in Go

\n
    \n
  • Local variables: 1-2 letters for obvious things (i for counter, s for string).
  • \n
  • Packages: one word, lowercase, no underscores (http, fmt).
  • \n
  • Exported identifiers: start with a capital letter (GetUser).
  • \n
  • Acronyms: written in the same case (HTTP, URL, not Http or Url).
  • \n
\n\n
// Bad\nfunc GetUserDataFromDatabaseByID(userID string) (UserData, error) {\n    var userDataFromDb UserData\n    // ...\n    return userDataFromDb, nil\n}\n\n// Good\nfunc GetUser(id string) (User, error) {\n    var u User\n    // ...\n    return u, nil\n}
\n\n

Tip: if a variable name takes more than 2 words, you are probably overcomplicating things.

\n\n

2. Formatting: Trust gofmt

\n\n

In Go, there are no "style wars". The gofmt command decides everything for you. This is not a recommendation — it is a standard. If you don't use gofmt, you are not writing Go, you are writing a "Go dialect".

\n\n

What does gofmt do?

\n
    \n
  • Sets indentation (tabs, not spaces).
  • \n
  • Removes unnecessary line breaks.
  • \n
  • Aligns comments.
  • \n
  • Removes unused imports (with the -s flag).
  • \n
\n\n
// Before gofmt\nfunc  add (a int,b int) int{\nreturn a+b\n}\n\n// After gofmt\nfunc add(a, b int) int {\n    return a + b\n}
\n\n

In any modern editor (VS Code, GoLand), gofmt runs automatically on save. If not, configure it. This will save hours of code review.

\n\n

3. Error Handling: Don't Ignore, Don't Panic

\n\n

Go has no exceptions (try-catch). Errors are simply values that need to be checked. This is one of the language's strongest features, if you use it correctly.

\n\n

Rules for Clean Error Handling

\n
    \n
  • Always check error. No _ for skipping.
  • \n
  • Do not use panic in library code. Only if the program cannot continue (e.g., a config file cannot be opened).
  • \n
  • Wrap errors using fmt.Errorf("context: %w", err) (Go 1.13+).
  • \n
\n\n
// Bad\nfunc ReadConfig(path string) *Config {\n    data, _ := ioutil.ReadFile(path) // ignoring the error\n    var cfg Config\n    json.Unmarshal(data, &cfg)\n    return &cfg\n}\n\n// Good\nfunc ReadConfig(path string) (*Config, error) {\n    data, err := ioutil.ReadFile(path)\n    if err != nil {\n        return nil, fmt.Errorf("read config: %w", err)\n    }\n    var cfg Config\n    if err := json.Unma                

Blogs

Book Recommendations