Skip to main content
โšก Calmops

Go Naming Conventions and Code Style

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

  1. Follow Go conventions - Use idiomatic Go style
  2. Use clear names - Intent should be obvious
  3. Keep names short - But not cryptic
  4. Use consistent style - Throughout your codebase
  5. Export intentionally - Only export what’s needed
  6. Document exported items - Add comments
  7. Use gofmt - Automatic formatting
  8. 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