// Follows Go community conventions and idiomatic patterns
# 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.6 matches