# Golang Development

> Write and review Go code with idiomatic project structure, errors, concurrency, testing, and performance guidance.

- **Type:** Skill
- **Install:** `agentstack add skill-brpaz-agent-skills-golang-development`
- **Verified:** Yes — security-reviewed for prompt injection and unsafe behavior
- **Seller:** [brpaz](https://agentstack.voostack.com/s/brpaz)
- **Installs:** 0
- **Category:** [Agent Skills](https://agentstack.voostack.com/c/agent-skills)
- **Latest version:** 0.1.0
- **License:** MIT
- **Upstream author:** [brpaz](https://github.com/brpaz)
- **Source:** https://github.com/brpaz/agent-skills/tree/main/skills/golang-development

## Install

```sh
agentstack add skill-brpaz-agent-skills-golang-development
```

Requires the [AgentStack CLI](https://agentstack.voostack.com/docs/cli). Works with Claude Code, Cursor, and any MCP-compatible agent.

## About

# Go (Golang) - Patterns and Guidance

Use this skill when writing, reviewing, or debugging Go code. Covers idiomatic Go patterns, project structure, concurrency, testing, and production-ready practices.

## When to Use

- Writing new Go packages, services, or CLI tools
- Reviewing or refactoring existing Go code for idiomatic style
- Debugging Go concurrency, error handling, or performance issues
- Setting up Go project structure, module layout, or test harness

## Go Philosophy

**Core principles:**
- Simplicity over cleverness
- Explicit over implicit
- Composition over inheritance
- Clear is better than clever
- Errors are values
- Concurrency is not parallelism

**Key idioms:**
- "Don't communicate by sharing memory; share memory by communicating"
- "A little copying is better than a little dependency"
- "The bigger the interface, the weaker the abstraction"
- "Make the zero value useful"
- "Accept interfaces, return structs"

## Project Structure

### Standard Layout

```
myproject/
├── cmd/                    # Main applications
│   ├── api/
│   │   └── main.go        # API server entry point
│   └── worker/
│       └── main.go        # Background worker entry point
├── internal/              # Private application code
│   ├── app/              # Application logic
│   ├── domain/           # Domain models
│   └── platform/         # Platform-specific code
├── pkg/                   # Public libraries (reusable)
│   ├── auth/
│   └── logger/
├── api/                   # API definitions (OpenAPI, protobuf)
├── web/                   # Web assets (templates, static files)
├── scripts/               # Build, install, analysis scripts
├── configs/               # Configuration files
├── deployments/           # Deployment configs (docker, k8s)
├── test/                  # Additional test data
├── docs/                  # Documentation
├── examples/              # Example code
├── tools/                 # Supporting tools
├── vendor/                # Vendored dependencies (optional)
├── go.mod                 # Module definition
├── go.sum                 # Dependency checksums
├── Makefile              # Build automation
└── README.md
```

**Key directories:**

| Directory | Purpose | Public/Private |
|-----------|---------|----------------|
| `cmd/` | Main applications (one per binary) | Private |
| `internal/` | Private application code (cannot be imported) | Private |
| `pkg/` | Public libraries (can be imported by others) | Public |
| `api/` | API definitions, schemas | Public |

**Rules:**
- `internal/` cannot be imported by external packages (enforced by Go compiler)
- One `main.go` per `cmd//` subdirectory
- Keep `pkg/` minimal. Place application code in `internal/` unless other modules must import it.
- Flat is better than nested - avoid deep hierarchies

### Minimal Project

For small projects:

```
myproject/
├── main.go
├── handler.go
├── service.go
├── repository.go
├── go.mod
├── go.sum
└── README.md
```

### Package Naming

```go
// ❌ BAD: Stuttering
auth.AuthService
user.UserRepository
http.HTTPClient

// ✅ GOOD: Concise
auth.Service
user.Repository
http.Client
```

**Rules:**
- Lowercase, single word
- No underscores or mixedCaps
- Avoid generic names (`util`, `common`, `base`, `helper`)
- Name by purpose, not type (`auth`, `logger`, not `structs`, `interfaces`)

## Error Handling

### Basic Pattern

```go
// ✅ GOOD: Immediate error check
result, err := doSomething()
if err != nil {
    return nil, err
}

// ❌ BAD: Deferred error check
result, err := doSomething()
// ... many lines ...
if err != nil {
    return nil, err
}
```

### Error Wrapping

```go
// ✅ GOOD: Wrap errors with context
func processUser(id int) error {
    user, err := fetchUser(id)
    if err != nil {
        return fmt.Errorf("failed to fetch user %d: %w", id, err)
    }
    
    if err := saveUser(user); err != nil {
        return fmt.Errorf("failed to save user %d: %w", id, err)
    }
    
    return nil
}

// Check wrapped errors
err := processUser(123)
if errors.Is(err, sql.ErrNoRows) {
    // Handle specific error
}
```

**Use `%w` for wrapping, `%v` for opaque errors.**

### Custom Errors

```go
// Define sentinel errors for expected conditions
var (
    ErrUserNotFound = errors.New("user not found")
    ErrInvalidInput = errors.New("invalid input")
    ErrUnauthorized = errors.New("unauthorized")
)

// Use in code
func getUser(id int) (*User, error) {
    if id  0 {
            result = append(result, item)
        }
    }
    return result
}

// ✅ GOOD: Reuse slice
func process(items []int, result []int) []int {
    result = result[:0] // Reset length
    for _, item := range items {
        if item > 0 {
            result = append(result, item)
        }
    }
    return result
}
```

### Sync.Pool for Temporary Objects

```go
var bufferPool = sync.Pool{
    New: func() interface{} {
        return new(bytes.Buffer)
    },
}

func processData(data []byte) {
    buf := bufferPool.Get().(*bytes.Buffer)
    defer func() {
        buf.Reset()
        bufferPool.Put(buf)
    }()
    
    buf.Write(data)
    // Use buf
}
```

### Profiling

```go
import _ "net/http/pprof"

func main() {
    go func() {
        log.Println(http.ListenAndServe("localhost:6060", nil))
    }()
    
    // Your application code
}
```

Access profiles:
- CPU: `http://localhost:6060/debug/pprof/profile`
- Heap: `http://localhost:6060/debug/pprof/heap`
- Goroutines: `http://localhost:6060/debug/pprof/goroutine`

Analyze:
```bash
go tool pprof http://localhost:6060/debug/pprof/profile
```

## Common Pitfalls

### Range Loop Variable Capture

```go
// ❌ BAD: Captures loop variable
for _, item := range items {
    go func() {
        fmt.Println(item) // All goroutines print last item
    }()
}

// ✅ GOOD: Capture explicitly
for _, item := range items {
    item := item // Reassign
    go func() {
        fmt.Println(item)
    }()
}

// ✅ GOOD: Pass as parameter
for _, item := range items {
    go func(i Item) {
        fmt.Println(i)
    }(item)
}
```

### Nil Interface Gotcha

```go
// ❌ BAD: Interface is not nil
func returnsError() error {
    var p *MyError = nil
    return p // error interface is not nil!
}

err := returnsError()
if err != nil {
    // This block executes!
}

// ✅ GOOD: Return explicit nil
func returnsError() error {
    var p *MyError = nil
    if p == nil {
        return nil
    }
    return p
}
```

### Defer in Loops

```go
// ❌ BAD: Defer accumulates
for _, file := range files {
    f, err := os.Open(file)
    if err != nil {
        return err
    }
    defer f.Close() // Won't run until function returns
}

// ✅ GOOD: Close immediately or use function
for _, file := range files {
    if err := processFile(file); err != nil {
        return err
    }
}

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

### Slice Append Gotcha

```go
// ❌ BAD: Original slice affected
a := []int{1, 2, 3}
b := a[:2]
b = append(b, 4)
// a is now [1, 2, 4]

// ✅ GOOD: Copy slice
a := []int{1, 2, 3}
b := make([]int, len(a[:2]))
copy(b, a[:2])
b = append(b, 4)
// a is still [1, 2, 3]
```

### Map Concurrent Access

```go
// ❌ BAD: Concurrent map writes cause panic
m := make(map[string]int)

go func() {
    m["key"] = 1
}()

go func() {
    m["key"] = 2
}()

// ✅ GOOD: Use sync.Map or mutex
var mu sync.Mutex
m := make(map[string]int)

go func() {
    mu.Lock()
    m["key"] = 1
    mu.Unlock()
}()

go func() {
    mu.Lock()
    m["key"] = 2
    mu.Unlock()
}()
```

## Production Guidance

### Logging

```go
// Use structured logging
import "go.uber.org/zap"

logger, _ := zap.NewProduction()
defer logger.Sync()

logger.Info("user logged in",
    zap.Int("user_id", 123),
    zap.String("ip", "192.168.1.1"),
)

logger.Error("failed to process request",
    zap.Error(err),
    zap.String("request_id", reqID),
)
```

### Configuration

```go
// Use environment variables or config files
import "github.com/spf13/viper"

func loadConfig() (*Config, error) {
    viper.SetConfigName("config")
    viper.SetConfigType("yaml")
    viper.AddConfigPath(".")
    viper.AddConfigPath("/etc/myapp/")
    
    viper.SetEnvPrefix("MYAPP")
    viper.AutomaticEnv()
    
    if err := viper.ReadInConfig(); err != nil {
        return nil, err
    }
    
    var cfg Config
    if err := viper.Unmarshal(&cfg); err != nil {
        return nil, err
    }
    
    return &cfg, nil
}
```

### Graceful Shutdown

```go
func main() {
    srv := &http.Server{
        Addr:    ":8080",
        Handler: handler,
    }
    
    go func() {
        if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
            log.Fatalf("listen: %s\n", err)
        }
    }()
    
    // Wait for interrupt signal
    quit := make(chan os.Signal, 1)
    signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
    <-quit
    
    log.Println("Shutting down server...")
    
    // Graceful shutdown with timeout
    ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
    defer cancel()
    
    if err := srv.Shutdown(ctx); err != nil {
        log.Fatal("Server forced to shutdown:", err)
    }
    
    log.Println("Server exiting")
}
```

### Database Connections

```go
import "database/sql"

func initDB() (*sql.DB, error) {
    db, err := sql.Open("postgres", connStr)
    if err != nil {
        return nil, err
    }
    
    // Connection pool settings
    db.SetMaxOpenConns(25)
    db.SetMaxIdleConns(5)
    db.SetConnMaxLifetime(5 * time.Minute)
    
    // Verify connection
    if err := db.Ping(); err != nil {
        return nil, err
    }
    
    return db, nil
}
```

### Context Propagation

```go
// Always pass context as first parameter
func fetchUser(ctx context.Context, userID int) (*User, error) {
    // Use context for cancellation, timeouts, and values
    if err := ctx.Err(); err != nil {
        return nil, err
    }
    
    // Pass context to downstream calls
    return db.GetUser(ctx, userID)
}

// HTTP handler
func handler(w http.ResponseWriter, r *http.Request) {
    ctx := r.Context()
    
    user, err := fetchUser(ctx, 123)
    if err != nil {
        http.Error(w, err.Error(), http.StatusInternalServerError)
        return
    }
    
    json.NewEncoder(w).Encode(user)
}
```

## Tools and Commands

### Essential Commands

```bash
# Format code
go fmt ./...

# Lint code (requires golangci-lint)
golangci-lint run

# Run tests
go test ./...

# Run tests with coverage
go test -cover ./...

# Run tests with race detector
go test -race ./...

# Run benchmarks
go test -bench=. ./...

# Build binary
go build -o myapp ./cmd/myapp

# Install dependencies
go mod download

# Tidy dependencies
go mod tidy

# Vendor dependencies
go mod vendor

# Update dependencies
go get -u ./...

# View documentation
go doc fmt.Println

# Run code
go run main.go
```

### Useful Tools

| Tool | Purpose | Install |
|------|---------|---------|
| `golangci-lint` | Comprehensive linter | `go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest` |
| `gofmt` | Code formatter | Built-in |
| `goimports` | Import formatter | `go install golang.org/x/tools/cmd/goimports@latest` |
| `staticcheck` | Static analysis | `go install honnef.co/go/tools/cmd/staticcheck@latest` |
| `govulncheck` | Vulnerability scanner | `go install golang.org/x/vuln/cmd/govulncheck@latest` |
| `dlv` | Debugger | `go install github.com/go-delve/delve/cmd/dlv@latest` |

### Makefile Example

```makefile
.PHONY: build test lint clean

build:
	go build -o bin/myapp ./cmd/myapp

test:
	go test -v -race -cover ./...

lint:
	golangci-lint run

clean:
	rm -rf bin/

install-tools:
	go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest
	go install golang.org/x/tools/cmd/goimports@latest

run:
	go run ./cmd/myapp
```

## Rules

- **ALWAYS check errors immediately** - defer error checks only when a later check is intentional and documented.
- **ALWAYS use `context.Context`** as the first parameter of functions that do I/O or long-running work.
- **ALWAYS close resources with defer** immediately after opening them (files, connections, etc.).
- **ALWAYS use `go fmt`** before committing code - no exceptions.
- **ALWAYS run tests with `-race` flag** to catch race conditions.
- **NEVER ignore errors** - at minimum, log them; never use `_` for error returns.
- **NEVER use `panic` for expected errors** - panic only for truly unrecoverable situations (programmer errors).
- **NEVER share memory by communicating** - communicate by sharing memory (use channels over mutexes when possible).
- **NEVER mutate slices/maps without understanding capacity** - be aware of shared underlying arrays.
- **Accept interfaces, return structs** - keep APIs flexible by accepting interfaces but returning concrete types.
- **Define interfaces where they're used**, not where they're implemented (consumer-side).
- **Keep interfaces small** - single-method interfaces are ideal.
- **Use table-driven tests** for comprehensive test coverage with minimal code.
- **Preallocate slices when size is known** - use `make([]T, 0, capacity)`.
- **Use `strings.Builder` for string concatenation** in loops - never concatenate strings with `+`.
- **Pass context to downstream functions** - never create new context in the middle of a call chain.
- **Name packages by purpose, not type** - `auth`, not `structs`; `logger`, not `interfaces`.
- **Avoid stuttering in names** - `user.Repository`, not `user.UserRepository`.

## Quick Reference

### Minimal HTTP Server

```go
package main

import (
    "encoding/json"
    "log"
    "net/http"
)

type User struct {
    ID   int    `json:"id"`
    Name string `json:"name"`
}

func main() {
    http.HandleFunc("/users", func(w http.ResponseWriter, r *http.Request) {
        users := []User{
            {ID: 1, Name: "Alice"},
            {ID: 2, Name: "Bob"},
        }
        
        w.Header().Set("Content-Type", "application/json")
        json.NewEncoder(w).Encode(users)
    })
    
    log.Fatal(http.ListenAndServe(":8080", nil))
}
```

### Minimal CLI App

```go
package main

import (
    "flag"
    "fmt"
    "os"
)

func main() {
    name := flag.String("name", "World", "name to greet")
    flag.Parse()
    
    fmt.Printf("Hello, %s!\n", *name)
}
```

### Module Initialization

```bash
# Initialize module
go mod init github.com/myorg/myapp

# Add dependency
go get github.com/pkg/errors

# Remove unused dependencies
go mod tidy
```

## Resources

- [Effective Go](https://go.dev/doc/effective_go)
- [Go Code Review Comments](https://github.com/golang/go/wiki/CodeReviewComments)
- [Go Proverbs](https://go-proverbs.github.io/)
- [Go Blog](https://go.dev/blog/)
- [Standard Library](https://pkg.go.dev/std)
- [Awesome Go](https://github.com/avelino/awesome-go)

## Inputs

- Go source files, module (`go.mod`), and description of the feature, bug, or refactoring goal
- Target Go version and any relevant package constraints

## Outputs

- Idiomatic Go code following standard project layout, explicit error handling, and table-driven tests

## Examples

```go
// Idiomatic error wrapping
func getUser(id int) (*User, error) {
    u, err := db.QueryUser(id)
    if err != nil {
        return nil, fmt.Errorf("getUser %d: %w", id, err)
    }
    return u, nil
}
```

## Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

- **Author:** [brpaz](https://github.com/brpaz)
- **Source:** [brpaz/agent-skills](https://github.com/brpaz/agent-skills)
- **License:** MIT

Install and usage instructions live in the source repository linked above.

## Pricing

- **Free** — Free

## Security capabilities

Automated source analysis of v0.1.0 — what this tool can access:

- **Network access:** yes
- **Filesystem access:** yes
- **Shell / process execution:** no
- **Environment & secrets:** no
- **Dynamic code execution:** no

*"Yes" means the capability is present in the source — more access means more to trust, not that it is unsafe.*


## Versions

- **0.1.0** — security scan: passed — Imported from the upstream source.

## Links

- Listing page: https://agentstack.voostack.com/l/skill-brpaz-agent-skills-golang-development
- Seller: https://agentstack.voostack.com/s/brpaz
- Browse the marketplace: https://agentstack.voostack.com/browse

---
Listed on AgentStack — the marketplace for AI agent skills and MCP servers. Every listing is security-reviewed. Creators keep 70%.
