Go Naming Conventions and Code Style
Go has strong conventions for naming and code style. Following these conventions makes your code more readable and maintainable, and helps other Go developers understand your code immediately. This guide covers Go’s naming conventions and style guidelines.
Package Naming
Package Naming Rules
// โ
Good: Short, lowercase, single word
package main
package fmt
package http
package json
// โ Bad: Multiple words, uppercase, underscores
package myPackage // Use camelCase
package my_package // Use underscores
package MyPackage // Use uppercase
Package Organization
myproject/
โโโ main.go // package main
โโโ config/
โ โโโ config.go // package config
โโโ database/
โ โโโ db.go // package database
โโโ handlers/
โ โโโ handlers.go // package handlers
โโโ utils/
โโโ utils.go // package utils
Package Naming Conventions
// โ
Good: Descriptive, single word
package database
package handlers
package models
package utils
// โ Bad: Generic or unclear
package common
package helper
package util
package misc
Function and Method Naming
Exported Functions (Public)
package mypackage
// โ
Good: Exported functions start with uppercase
func NewUser(name string) *User {
return &User{Name: name}
}
func (u *User) Save() error {
// Save user
return nil
}
func (u *User) GetEmail() string {
return u.Email
}
// โ Bad: Lowercase (unexported)
func newUser(name string) *User {
return &User{Name: name}
}
Unexported Functions (Private)
package mypackage
// โ
Good: Unexported functions start with lowercase
func validateEmail(email string) bool {
// Validate email
return true
}
func (u *User) setDefaults() {
// Set default values
}
// โ Bad: Uppercase (exported when should be private)
func ValidateEmail(email string) bool {
// Validate email
return true
}
Naming Patterns
package mypackage
// Constructor pattern
func NewUser(name string) *User {
return &User{Name: name}
}
// Getter pattern (no Get prefix for simple getters)
func (u *User) Name() string {
return u.name
}
// Setter pattern (use Set prefix)
func (u *User) SetName(name string) {
u.name = name
}
// Predicate pattern (use Is, Has, Can prefix)
func (u *User) IsActive() bool {
return u.Active
}
func (u *User) HasPermission(perm string) bool {
return true
}
// Receiver pattern (use With prefix)
func (u *User) WithEmail(email string) *User {
u.Email = email
return u
}
Variable and Constant Naming
Local Variables
package main
func main() {
// โ
Good: Short, descriptive names
user := getUser()
count := 0
isActive := true
// โ Bad: Single letter (except in loops), unclear
u := getUser()
c := 0
ia := true
}
Loop Variables
package main
func main() {
// โ
Good: Single letter for loop index
for i := 0; i < 10; i++ {
fmt.Println(i)
}
// โ
Good: Descriptive for range
for index, value := range items {
fmt.Println(index, value)
}
// โ
Good: Blank identifier for unused
for _, value := range items {
fmt.Println(value)
}
// โ Bad: Unclear names
for idx := 0; idx < 10; idx++ {
fmt.Println(idx)
}
}
Constants
package mypackage
// โ
Good: Uppercase with underscores
const (
MaxRetries = 3
DefaultTimeout = 30
MinPasswordLength = 8
)
// โ Bad: Lowercase, unclear
const (
maxRetries = 3
default_timeout = 30
minPasswordLength = 8
)
Interface Naming
Single Method Interfaces
package io
// โ
Good: Method name + "er"
type Reader interface {
Read(p []byte) (n int, err error)
}
type Writer interface {
Write(p []byte) (n int, err error)
}
type Closer interface {
Close() error
}
// โ Bad: Generic names
type ReadInterface interface {
Read(p []byte) (n int, err error)
}
Multi-Method Interfaces
package io
// โ
Good: Descriptive names
type ReadWriter interface {
Read(p []byte) (n int, err error)
Write(p []byte) (n int, err error)
}
type ReadCloser interface {
Read(p []byte) (n int, err error)
Close() error
}
// โ Bad: Generic names
type Interface interface {
Read(p []byte) (n int, err error)
Write(p []byte) (n int, err error)
}
Type Naming
Struct Naming
package models
// โ
Good: Descriptive, exported
type User struct {
ID int
Name string
Email string
}
type Product struct {
ID int
Name string
Price float64
}
// โ Bad: Generic, unclear
type Data struct {
ID int
Name string
Email string
}
type Item struct {
ID int
Name string
Price float64
}
Struct Field Naming
package models
// โ
Good: Clear, exported fields
type User struct {
ID int
FirstName string
LastName string
Email string
IsActive bool
}
// โ Bad: Unclear, inconsistent
type User struct {
id int
first_name string
last_name string
email string
active bool
}
Error Naming
Error Variables
package mypackage
import "errors"
// โ
Good: Prefix with "Err"
var (
ErrNotFound = errors.New("not found")
ErrInvalidInput = errors.New("invalid input")
ErrUnauthorized = errors.New("unauthorized")
)
// โ Bad: No prefix, unclear
var (
NotFound = errors.New("not found")
InvalidInput = errors.New("invalid input")
Unauthorized = errors.New("unauthorized")
)
Error Types
package mypackage
// โ
Good: Suffix with "Error"
type ValidationError struct {
Field string
Message string
}
type DatabaseError struct {
Operation string
Err error
}
// โ Bad: No suffix, unclear
type Validation struct {
Field string
Message string
}
Code Style Guidelines
Indentation and Formatting
// โ
Good: Use tabs (Go standard)
func main() {
if x > 0 {
fmt.Println("positive")
}
}
// โ Bad: Use spaces (inconsistent)
func main() {
if x > 0 {
fmt.Println("positive")
}
}
Line Length
// โ
Good: Reasonable line length
func processUser(user *User) error {
if user == nil {
return ErrNilUser
}
return nil
}
// โ Bad: Too long
func processUser(user *User) error { if user == nil { return ErrNilUser } ; return nil }
Brace Placement
// โ
Good: Opening brace on same line
func main() {
if x > 0 {
fmt.Println("positive")
}
}
// โ Bad: Opening brace on new line (not Go style)
func main()
{
if x > 0
{
fmt.Println("positive")
}
}
Comments
package mypackage
// โ
Good: Comment starts with function name
// User represents a user in the system
type User struct {
ID int
Name string
}
// NewUser creates a new user with the given name
func NewUser(name string) *User {
return &User{Name: name}
}
// โ Bad: Unclear comments
// This is a user
type User struct {
ID int
Name string
}
// Make a new user
func NewUser(name string) *User {
return &User{Name: name}
}
Receiver Names
package mypackage
// โ
Good: Short receiver name (1-2 letters)
func (u *User) Save() error {
// Save user
return nil
}
func (p *Product) GetPrice() float64 {
return p.Price
}
// โ Bad: Long receiver name
func (user *User) Save() error {
// Save user
return nil
}
func (product *Product) GetPrice() float64 {
return product.Price
}
Import Organization
package main
import (
// โ
Good: Organized imports
"fmt"
"os"
"github.com/user/package1"
"github.com/user/package2"
)
// โ Bad: Disorganized imports
import (
"github.com/user/package2"
"fmt"
"github.com/user/package1"
"os"
)
Practical Examples
Good Style Example
package users
import (
"errors"
"fmt"
)
var ErrNotFound = errors.New("user not found")
type User struct {
ID int
Name string
Email string
}
// NewUser creates a new user
func NewUser(name, email string) *User {
return &User{
Name: name,
Email: email,
}
}
// Save saves the user
func (u *User) Save() error {
if u.Name == "" {
return errors.New("name is required")
}
return nil
}
// String returns a string representation of the user
func (u *User) String() string {
return fmt.Sprintf("User{ID: %d, Name: %s, Email: %s}", u.ID, u.Name, u.Email)
}
Bad Style Example
package users
import (
"errors"
"fmt"
)
var NotFound = errors.New("user not found")
type user struct {
id int
name string
email string
}
// new_user creates a new user
func new_user(name, email string) *user {
return &user{
name: name,
email: email,
}
}
// save saves the user
func (u *user) save() error {
if u.name == "" {
return errors.New("name is required")
}
return nil
}
// string returns a string representation of the user
func (u *user) string() string {
return fmt.Sprintf("user{id: %d, name: %s, email: %s}", u.id, u.name, u.email)
}
Best Practices
โ Good Practices
- Follow Go conventions - Use idiomatic Go style
- Use clear names - Intent should be obvious
- Keep names short - But not cryptic
- Use consistent style - Throughout your codebase
- Export intentionally - Only export what’s needed
- Document exported items - Add comments
- Use gofmt - Automatic formatting
- Review code - Ensure consistency
โ Anti-Patterns
// โ Bad: Inconsistent naming
type User struct {
ID int
user_name string
EmailAddress string
}
// โ Bad: Unclear abbreviations
func (u *User) GetUNm() string {
return u.user_name
}
// โ Bad: Generic names
type Data struct {
Value interface{}
}
// โ Bad: No documentation
func (u *User) Process() {
// ...
}
Summary
Go naming conventions and style are important:
- Use lowercase for package names
- Export with uppercase, unexport with lowercase
- Use descriptive names
- Follow interface naming patterns
- Use consistent formatting
- Document exported items
- Use gofmt for automatic formatting
Following these conventions makes your Go code more professional and maintainable.
Comments