Go Idiomatic Patterns

// Follows Go community conventions and idiomatic patterns

GoBest PracticesBackend
Highly Rated
Community Verified

// detailed.guidelines

# Go Idiomatic Patterns

Write idiomatic Go code that follows community standards.

## Always Handle Errors

```go
// Bad - ignoring errors
data, _ := os.ReadFile("file.txt")

// Good - handle every error
data, err := os.ReadFile("file.txt")
if err != nil {
    return fmt.Errorf("failed to read file: %w", err)
}
```

## Use defer for Cleanup

```go
func processFile(filename string) error {
    f, err := os.Open(filename)
    if err != nil {
        return err
    }
    defer f.Close()  // Cleanup automatically
    
    // Process file...
    return nil
}

// Multiple defers execute in reverse order
func example() {
    defer fmt.Println("world")
    defer fmt.Println("hello")
    // Prints: hello world
}
```

## Accept Interfaces, Return Structs

```go
// Bad - accepting concrete type
func SaveUser(db *sql.DB, user User) error {
    // Limited to sql.DB only
}

// Good - accepting interface
type UserRepository interface {
    Save(user User) error
}

func SaveUser(repo UserRepository, user User) error {
    return repo.Save(user)
}
```

## Use Short Variable Names

```go
// Bad
func (repository *UserRepository) FindByIdentifier(userIdentifier int) (*UserEntity, error) {
    // ...
}

// Good
func (r *UserRepository) FindByID(id int) (*User, error) {
    // ...
}

// Common conventions
// i, j, k for indexes
// r for reader
// w for writer
// err for errors
```

## Group Imports

```go
import (
    // Standard library first
    "context"
    "fmt"
    "time"
    
    // External packages
    "github.com/gin-gonic/gin"
    "github.com/lib/pq"
    
    // Your packages last
    "myapp/internal/models"
    "myapp/pkg/utils"
)
```

## Use Context for Cancellation

```go
func ProcessRequest(ctx context.Context, id string) error {
    // Check for cancellation
    select {
    case <-ctx.Done():
        return ctx.Err()
    default:
    }
    
    // Do work...
    
    // Pass context to child functions
    return fetchData(ctx, id)
}
```

## Initialize Structs Explicitly

```go
// Bad - unclear what's being set
user := User{"Alice", 30, "alice@example.com"}

// Good - explicit field names
user := User{
    Name:  "Alice",
    Age:   30,
    Email: "alice@example.com",
}
```

## Use Table-Driven Tests

```go
func TestAdd(t *testing.T) {
    tests := []struct {
        name     string
        a, b     int
        expected int
    }{
        {"positive numbers", 2, 3, 5},
        {"with zero", 0, 5, 5},
        {"negative numbers", -2, -3, -5},
    }
    
    for _, tt := range tests {
        t.Run(tt.name, func(t *testing.T) {
            result := Add(tt.a, tt.b)
            if result != tt.expected {
                t.Errorf("Add(%d, %d) = %d; want %d",
                    tt.a, tt.b, result, tt.expected)
            }
        })
    }
}
```

## Use Goroutines Wisely

```go
// Use sync.WaitGroup for coordination
var wg sync.WaitGroup

for i := 0; i < 10; i++ {
    wg.Add(1)
    go func(id int) {
        defer wg.Done()
        process(id)
    }(i)  // Pass i as parameter
}

wg.Wait()  // Wait for all goroutines

// Use channels for communication
results := make(chan Result, 10)

go func() {
    defer close(results)
    for i := 0; i < 10; i++ {
        results <- process(i)
    }
}()

for result := range results {
    fmt.Println(result)
}
```

## Check for Empty Slices/Maps

```go
// Good - check length
if len(items) == 0 {
    return errors.New("no items")
}

// For nil checks
var items []string
if items == nil {
    items = []string{}
}
```

## Use make for Slices/Maps When Size Known

```go
// Bad - repeated allocations
var items []int
for i := 0; i < 1000; i++ {
    items = append(items, i)
}

// Good - pre-allocate
items := make([]int, 0, 1000)
for i := 0; i < 1000; i++ {
    items = append(items, i)
}

// Or if filling directly
items := make([]int, 1000)
for i := range items {
    items[i] = i
}
```

## Return Early to Avoid Nesting

```go
// Bad - nested ifs
func process(user *User) error {
    if user != nil {
        if user.Active {
            if user.Email != "" {
                return sendEmail(user.Email)
            }
        }
    }
    return nil
}

// Good - early returns
func process(user *User) error {
    if user == nil {
        return nil
    }
    if !user.Active {
        return nil
    }
    if user.Email == "" {
        return nil
    }
    return sendEmail(user.Email)
}
```

## Use Meaningful Zero Values

```go
type Config struct {
    Port    int    // 0 is often valid
    Timeout int    // 0 means no timeout
    Debug   bool   // false is default
}

// Usage works without initialization
var cfg Config
// Port: 0, Timeout: 0, Debug: false
```

## Embed for Composition

```go
type Reader interface {
    Read(p []byte) (n int, err error)
}

type Writer interface {
    Write(p []byte) (n int, err error)
}

// Embed interfaces
type ReadWriter interface {
    Reader
    Writer
}

// Embed structs
type User struct {
    ID   int
    Name string
}

type Admin struct {
    User           // Embedded
    Permissions []string
}

admin := Admin{
    User: User{ID: 1, Name: "Alice"},
    Permissions: []string{"read", "write"},
}
fmt.Println(admin.Name)  // Access embedded fields directly
```

Idiomatic Go is simple, explicit, and easy to read.