Introduction: Why Clean Code in Go is Not a Luxury, but a Necessity
\n\nGo (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.
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\n1. Proper Variable Names: Short and Clear
\n\nIn 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.
Basic Naming Rules in Go
\n- \n
- Local variables: 1-2 letters for obvious things (
ifor counter,sfor 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, notHttporUrl). \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\nTip: if a variable name takes more than 2 words, you are probably overcomplicating things.
\n\n2. Formatting: Trust gofmt
\n\nIn 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".
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
-sflag). \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\nIn any modern editor (VS Code, GoLand), gofmt runs automatically on save. If not, configure it. This will save hours of code review.
3. Error Handling: Don't Ignore, Don't Panic
\n\nGo 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\nRules for Clean Error Handling
\n- \n
- Always check
error. No_for skipping. \n - Do not use
panicin 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
// 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