“Any fool can write code that a computer can understand. Good programmers write code that humans can understand.”
— Martin Fowler
Go is known for its simplicity — but clean, readable, and maintainable code doesn’t write itself. Whether you're building microservices, CLIs, or REST APIs, good Go code follows a few golden rules.
Let’s walk through some practical tips and real Go examples to help you write cleaner and more idiomatic code.
🧠 1. Use Clear, Descriptive Names
Don’t try to be clever. Clarity beats brevity every time.
❌ Bad:
func f(u int) {
fmt.Println(u)
}
✅ Good:
func printUserID(userID int) {
fmt.Println(userID)
}
Use camelCase for variables and functions. Reserve abbreviations for widely recognized terms like id, url, db.
⚡ 2. Keep Functions Focused and Small
One function = one responsibility. Break up logic when possible.
❌ Bad:
func handleOrder(o Order) {
validate(o)
saveToDB(o)
sendEmail(o)
}
✅ Good:
func handleOrder(o Order) {
if err := validate(o); err != nil {
log.Println("Validation failed:", err)
return
}
if err := storeOrder(o); err != nil {
log.Println("Database error:", err)
return
}
notifyCustomer(o)
}
func storeOrder(o Order) error {
// logic to store in database
return nil
}
func notifyCustomer(o Order) {
// logic to send email
}
Short, specific functions are easier to test and reuse.
🧰 3. Format Code with gofmt or goimports
No one wants to argue about tabs or spacing. Let the tools handle it.
gofmt -w .
goimports -w .
They’ll format your code and manage your imports automatically. Clean code is also consistent code.
💬 4. Comment Why, Not What
Don’t write comments for things that are self-explanatory. Focus on why a decision was made.
❌ Bad:
// Add two integers
func Add(x, y int) int {
return x + y
}
✅ Better:
// Add returns the sum of x and y.
// Used in the calculator to combine user inputs.
func Add(x, y int) int {
return x + y
}
Use Go-style doc comments for exported functions and packages.
✅ 5. Write Tests (and Use Table-Driven Ones)
Go makes testing easy — no excuses.
import "testing"
func TestAdd(t *testing.T) {
tests := []struct {
name string
a, b int
want int
}{
{"positive", 2, 3, 5},
{"zero", 0, 0, 0},
{"negative", -2, -3, -5},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := Add(tt.a, tt.b); got != tt.want {
t.Errorf("Add(%d, %d) = %d; want %d", tt.a, tt.b, got, tt.want)
}
})
}
}
Use go test -cover to check coverage, and don’t forget BenchmarkX when performance matters.
♻️ 6. Don't Over-Abstract
Avoid the trap of premature abstraction. Repetition isn’t always evil.
❌ Over-abstracted:
func logStart() { fmt.Println("starting...") }
func logEnd() { fmt.Println("ending...") }
✅ Simple and reusable:
func logPhase(phase string) {
fmt.Printf("%s...\n", phase)
}
Rule of thumb: if code is used more than twice and not tied to specific context — abstract it. Otherwise, duplicate with purpose.
📁 7. Structure Projects by Responsibility
Go prefers flat, not deep. Organize based on what the code does.
/cmd/myapp // app entry point
/internal/handlers // HTTP or CLI handlers
/internal/db // DB logic
/pkg/utils // reusable helpers
Avoid generic package names like common or base.
👥 8. Code Like Someone Else Will Maintain It
Because someday… they will (and it might be you).
Ask yourself:
Would a junior developer understand this code?
Is it easy to test or extend?
Are errors clear and helpful?
Your code should invite collaboration, not fear.
🔚 Final Thoughts
Writing clean Go code isn’t hard, but it requires discipline.
✅ Use meaningful names
✅ Write focused, testable functions
✅ Format everything with gofmt
✅ Test like you mean it
✅ Comment only when needed
✅ Keep it simple, not clever
🗣️ What’s your favorite Go coding tip?
Drop it in the comments!
👉 Follow me for more Go content, real-world project tips, and practical dev advice.
Top comments (0)