# Go Structure & Build Optimization Guide

## Build Time

### Measuring build time
```bash
# Full build
time go build ./...

# With caching cleared
go clean -cache && time go build ./...

# Verbose (see package compilation order)
go build -v ./... 2>&1
```

### What affects build time
1. **Dependency count**: More packages = more compilation
2. **CGo**: Each CGo package requires C compiler invocation (~10x slower than pure Go)
3. **Code generation**: `go generate` steps that produce large files
4. **Template-heavy code**: Generic instantiation (Go 1.18+) adds compilation
5. **Build cache misses**: Changed files invalidate dependent packages

### Reducing build time
- Remove unused dependencies: `go mod tidy`
- Replace CGo with pure Go alternatives when possible
- Split large packages: compiler parallelizes across packages
- Use build tags to exclude heavy code from dev builds

## init() Functions

### Problems with init()
- Run at import time — before `main()`
- Order is deterministic but fragile (alphabetical within package, import order across packages)
- Side effects (DB connections, file I/O, HTTP calls) slow startup
- Cannot return errors — panics are the only escape hatch

### Finding init() functions
```bash
grep -rn 'func init()' --include='*.go' . | grep -v _test.go | grep -v vendor
```

### Replacing with lazy initialization
```go
// Before: blocks startup
var db *sql.DB
func init() {
    var err error
    db, err = sql.Open("postgres", os.Getenv("DATABASE_URL"))
    if err != nil {
        log.Fatal(err)
    }
}

// After: lazy, error-returning
var (
    db     *sql.DB
    dbOnce sync.Once
    dbErr  error
)

func getDB() (*sql.DB, error) {
    dbOnce.Do(func() {
        db, dbErr = sql.Open("postgres", os.Getenv("DATABASE_URL"))
    })
    return db, dbErr
}
```

## Dependency Management

### Audit dependencies
```bash
# Total dependency count
go list -m all | wc -l

# Direct vs indirect
grep -c '^\t[^ ]' go.mod          # direct
grep -c '// indirect' go.mod       # indirect

# Dependency tree
go mod graph | head -30

# Find why a dependency exists
go mod why -m <module>
```

### Reduce dependencies
1. `go mod tidy` — remove unused
2. Check if stdlib can replace a dep (e.g., `errors.Is/As` instead of `pkg/errors`)
3. For small utilities, consider copying the needed code instead of importing the whole package
4. Replace CGo dependencies with pure Go alternatives

## Package Organization

### Signs of a god package
- >20 files in one package
- >50% of other packages import it
- Mix of unrelated types and functions
- Circular dependency workarounds (interface in a third package)

### Splitting strategy
```
// Before: everything in pkg/
pkg/
  models.go
  handlers.go
  db.go
  cache.go
  utils.go

// After: cohesive packages
pkg/
  model/     # data types
  handler/   # HTTP handlers
  store/     # database access
  cache/     # caching layer
```

### Internal packages
Use `internal/` to prevent external imports:
```
project/
  internal/
    parser/    # only importable by project code
  pkg/
    api/       # importable by anyone
```

## CGo Optimization

### CGo overhead
Each CGo call has ~200ns overhead (Go→C→Go transition). In a hot loop, this is devastating.

### Strategies
1. **Batch calls**: Accumulate work, make one CGo call instead of many
2. **Pure Go alternative**: Many C libraries have Go equivalents
3. **Shared memory**: Pass large buffers instead of many small calls
4. **Build tags**: `//go:build !cgo` for pure Go fallback

### Finding CGo usage
```bash
grep -rn 'import "C"' --include='*.go' . | grep -v vendor
grep -rn '#include' --include='*.go' . | grep -v vendor
```

## Compiler Flags for Performance

### Binary size reduction
```bash
go build -ldflags="-s -w" -o app  # strip debug info, 30-40% smaller
```

### Build-time variable injection
```bash
go build -ldflags="-X main.version=1.0.0 -X main.commit=$(git rev-parse HEAD)"
```

### Static linking (no cgo dependencies)
```bash
CGO_ENABLED=0 go build -o app              # pure Go static binary
CGO_ENABLED=1 go build -tags netgo \        # static with cgo
  -ldflags="-linkmode=external -extldflags '-static'"
```

### Cross-compilation
```bash
GOOS=linux GOARCH=amd64 go build -o app-linux
GOOS=linux GOARCH=arm64 go build -o app-arm64
```

### Performance analysis flags
```bash
go build -gcflags='-m' ./...                           # escape analysis
go build -gcflags='-m -m' ./...                        # verbose escape reasons
go build -gcflags='-d=ssa/check_bce/debug=1' ./...     # bounds check elimination
```

For the full compiler flags reference, see `${CLAUDE_PLUGIN_ROOT}/references/compiler-flags/reference.md`.
