Commit 7371c1922df9
Changed files (2)
dots
.config
claude
skills
golang
dots/.config/claude/skills/golang/nix-integration.md
@@ -0,0 +1,301 @@
+# Go and Nix Integration
+
+This document provides guidance when working with Go projects that use Nix for building and packaging.
+
+## When to Use This Document
+
+This integration guide applies when:
+- You detect `flake.nix`, `default.nix`, or `shell.nix` in the project root
+- The project is in a Nix-managed repository (contains `/pkgs` or `/tools` with Nix definitions)
+- The user explicitly mentions Nix or NixOS
+
+## Project Structure for Nix-Based Go Projects
+
+### Typical Layout in ~/src/home Repository
+```
+tools/<tool-name>/
+├── cmd/
+│ └── <tool-name>/
+│ └── main.go # Entry point
+├── internal/ # Private application code
+│ ├── config/
+│ ├── handlers/
+│ └── models/
+├── pkg/ # Public library code (if needed)
+├── go.mod # Module definition
+├── go.sum # Dependency checksums
+├── README.md
+└── *_test.go # Tests alongside code
+
+pkgs/<tool-name>/
+└── default.nix # Nix package definition
+```
+
+### Multi-Architecture Support
+- Build for x86_64-linux and aarch64-linux
+- Nix handles cross-compilation automatically
+- Custom packages exposed via overlays in `/overlays`
+
+## Building Go Tools with Nix
+
+### Build Commands
+```bash
+# Build a Go tool package
+nix build .#<package-name>
+
+# Install to profile
+nix profile install .#<package-name>
+
+# Check what would be built
+nix build .#<package-name> --dry-run
+
+# Build for specific architecture
+nix build .#<package-name> --system aarch64-linux
+```
+
+### Development Shell
+```bash
+# Enter dev shell with Go tools available
+nix develop
+
+# Run tests in development shell
+cd tools/<tool-name>
+go test ./...
+
+# Format code (pre-commit hooks use gofmt)
+gofmt -w .
+```
+
+## Package Definition with buildGoModule
+
+### Basic Package Definition
+```nix
+# In pkgs/<package-name>/default.nix
+{ lib, buildGoModule }:
+
+buildGoModule {
+ pname = "tool-name";
+ version = "1.0.0";
+
+ # Source from tools directory
+ src = ../../tools/tool-name;
+
+ # Vendor hash for dependency management
+ # Set to lib.fakeHash initially, then update with actual hash
+ vendorHash = "sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=";
+
+ # Build flags (optional)
+ ldflags = [
+ "-s"
+ "-w"
+ "-X main.version=${version}"
+ ];
+
+ # Metadata
+ meta = with lib; {
+ description = "Tool description";
+ homepage = "https://github.com/user/repo";
+ license = licenses.mit;
+ maintainers = [ ];
+ };
+}
+```
+
+### Updating Vendor Hash
+```bash
+# Initial build to get vendor hash
+nix build .#<package-name> 2>&1 | grep "got:"
+
+# Copy the hash from the error message to vendorHash in default.nix
+```
+
+### Common buildGoModule Options
+```nix
+buildGoModule {
+ # ... other options ...
+
+ # Exclude certain packages from building
+ excludedPackages = [ "cmd/debug-tool" ];
+
+ # Skip tests during build
+ doCheck = false;
+
+ # Additional build inputs
+ nativeBuildInputs = [ pkg-config ];
+ buildInputs = [ sqlite ];
+
+ # Post-install steps
+ postInstall = ''
+ installShellCompletion --cmd tool-name \
+ --bash <($out/bin/tool-name completion bash) \
+ --zsh <($out/bin/tool-name completion zsh)
+ '';
+
+ # CGO settings
+ CGO_ENABLED = 0; # Static binary
+}
+```
+
+## Testing in Nix Environment
+
+### Running Tests
+```bash
+# From repository root
+nix develop
+cd tools/<tool-name>
+go test ./...
+
+# With coverage
+go test -cover ./...
+
+# Verbose output
+go test -v ./...
+```
+
+### Pre-commit Hooks
+The repository uses pre-commit hooks for Go:
+- `gofmt` for formatting
+- Can be installed with `make install-hooks`
+
+```bash
+# Install hooks
+make install-hooks
+
+# Run pre-commit checks manually
+make pre-commit
+```
+
+## Integration with Repository Build System
+
+### Makefile Targets (if available)
+```bash
+# Build Go tools via Nix
+make build
+
+# Run tests
+make test
+
+# Format code
+make fmt
+
+# Clean build artifacts
+make clean
+```
+
+## Dependency Management with Nix
+
+### Adding Dependencies
+```bash
+# Add Go dependency normally
+cd tools/<tool-name>
+go get github.com/pkg/errors
+go mod tidy
+
+# Update Nix package vendorHash
+nix build .#<package-name> # Will show new hash
+```
+
+### Updating Dependencies
+```bash
+# Update Go modules
+cd tools/<tool-name>
+go get -u ./...
+go mod tidy
+
+# Update vendorHash in pkgs/<package-name>/default.nix
+# Rebuild to verify
+nix build .#<package-name>
+```
+
+## Common Patterns
+
+### Tool with Subcommands
+```nix
+buildGoModule {
+ pname = "multi-tool";
+ version = "1.0.0";
+ src = ../../tools/multi-tool;
+
+ # Build only the main command
+ subPackages = [ "cmd/multi-tool" ];
+
+ vendorHash = "sha256-...";
+}
+```
+
+### Tool with Static Assets
+```nix
+buildGoModule {
+ pname = "web-tool";
+ version = "1.0.0";
+ src = ../../tools/web-tool;
+
+ vendorHash = "sha256-...";
+
+ # Embed static assets
+ preBuild = ''
+ cp -r ${../../tools/web-tool/assets} ./assets
+ '';
+}
+```
+
+### Tool Requiring External Dependencies
+```nix
+{ lib, buildGoModule, pkg-config, sqlite }:
+
+buildGoModule {
+ pname = "db-tool";
+ version = "1.0.0";
+ src = ../../tools/db-tool;
+
+ nativeBuildInputs = [ pkg-config ];
+ buildInputs = [ sqlite ];
+
+ vendorHash = "sha256-...";
+
+ # Enable CGO for database drivers
+ CGO_ENABLED = 1;
+}
+```
+
+## Troubleshooting
+
+### Vendor Hash Mismatch
+```bash
+# Problem: vendorHash error during build
+# Solution: Update hash with the one shown in error message
+nix build .#<package-name> 2>&1 | grep "got:"
+```
+
+### Missing Dependencies
+```bash
+# Problem: Missing system libraries
+# Solution: Add to buildInputs or nativeBuildInputs
+buildInputs = [ pkg-config sqlite ];
+```
+
+### Test Failures in Nix Build
+```bash
+# Problem: Tests fail during nix build but pass with `go test`
+# Solution: Disable tests in package definition
+doCheck = false;
+
+# Or fix test dependencies
+checkInputs = [ git ];
+```
+
+## Best Practices for Nix + Go
+
+1. **Keep go.mod/go.sum updated**: Always run `go mod tidy` after dependency changes
+2. **Update vendorHash promptly**: Don't commit package definitions with incorrect hashes
+3. **Test in nix develop**: Ensure tools work in Nix environment before building package
+4. **Use proper source paths**: Reference tool sources with relative paths (e.g., `../../tools/name`)
+5. **Document build requirements**: Note any special build flags or dependencies in README
+6. **Leverage overlays**: Add custom packages to overlays for repository-wide availability
+7. **Version consistently**: Keep version in sync between go.mod and default.nix
+
+## Resources
+
+- Nix Manual: https://nixos.org/manual/nix/stable/
+- buildGoModule documentation: https://nixos.org/manual/nixpkgs/stable/#sec-language-go
+- Repository structure: See `/pkgs` and `/tools` directories for examples
dots/.config/claude/skills/golang/SKILL.md
@@ -1,101 +1,315 @@
---
-name: golang
-description: Go development best practices and patterns. USE WHEN writing Go code, designing Go projects, working with Go tools, testing, or integrating Go with Nix.
+name: Golang
+description: Go development best practices and patterns. USE WHEN writing Go code, designing Go projects, working with Go tools, testing, or Go package development.
---
# Go Development Best Practices
## Purpose
-Guide Go development following best practices and project conventions.
+Guide Go development following official standards, community best practices, and idiomatic patterns from Effective Go and the Go team's code review guidelines.
-## Project Structure (for tools in ~/src/home)
+## Core Principles
-### Standard Layout
+1. **Clarity over cleverness**: Write simple, readable code that's easy to maintain
+2. **Gofmt is law**: Always format code with `gofmt` - no exceptions
+3. **Handle errors explicitly**: Errors are values that must be deliberately processed
+4. **Share by communicating**: Use channels and goroutines for concurrency
+5. **Prefer composition**: Embed types and use interfaces over inheritance patterns
+
+## Standard Project Structure
+
+### Recommended Layout
```
-tools/<tool-name>/
+myproject/
├── cmd/
-│ └── <tool-name>/
-│ └── main.go # Entry point
+│ └── myapp/
+│ └── main.go # Application entry points
├── internal/ # Private application code
│ ├── config/
│ ├── handlers/
│ └── models/
-├── pkg/ # Public library code (if needed)
+├── pkg/ # Public library code (optional)
+│ └── utils/
+├── api/ # API definitions (OpenAPI, protobuf)
+├── configs/ # Configuration files
+├── docs/ # Documentation
+├── scripts/ # Build and analysis scripts
+├── test/ # Additional test data
├── go.mod # Module definition
├── go.sum # Dependency checksums
├── README.md
└── *_test.go # Tests alongside code
```
-### Integration with Nix
-- Tools source: `/tools/<tool-name>/`
-- Custom packages: `/pkgs/` (exposed via overlays)
-- Testing: Run from tool directory with `go test ./...`
-- Multi-arch: Build for x86_64-linux and aarch64-linux
+### Key Directories
-## Best Practices
+- **`cmd/`**: Main applications - each subdirectory is a separate binary
+- **`internal/`**: Private code that cannot be imported by other projects
+- **`pkg/`**: Public libraries that can be imported by external projects
+- **`api/`**: API specifications and protocol definitions
-### Code Organization
+**Start simple**: Don't create all directories upfront. Add structure as complexity grows.
-#### Package Naming
-- Use lowercase, single-word names
-- No underscores or camelCase
-- Example: `package config`, `package handler`
+## Writing Idiomatic Go
-#### File Naming
-- Use lowercase with underscores for multi-word names
-- Example: `http_client.go`, `user_handler.go`
-- Test files: `*_test.go`
+### Formatting and Style
-### Writing Idiomatic Go
+**Always use gofmt**
+```bash
+# Format all files in current directory and subdirectories
+gofmt -w .
+
+# Use goimports to also manage imports
+goimports -w .
+```
+
+**Follow standard formatting**:
+- Use tabs for indentation (gofmt handles this)
+- No strict line length, but wrap long lines for readability
+- Group imports: standard library, third-party, local
-#### Error Handling
```go
-// Good: Wrap errors with context
-if err := doSomething(); err != nil {
+import (
+ "fmt"
+ "os"
+
+ "github.com/pkg/errors"
+ "github.com/spf13/cobra"
+
+ "myproject/internal/config"
+)
+```
+
+### Naming Conventions
+
+**Packages**
+- Lowercase, single-word names: `package config`, `package handler`
+- No underscores or mixedCaps in package names
+- Choose clear, concise names that describe the package's purpose
+
+**Variables and Functions**
+- Use `mixedCaps` or `MixedCaps`, never underscores
+- Exported names start with uppercase: `UserController`, `ProcessData`
+- Private names start with lowercase: `userCount`, `processRequest`
+- Short names in small scopes: `i`, `err`, `ok`, `buf`
+- Descriptive names in larger scopes: `userRepository`, `configManager`
+
+**Getters and Setters**
+```go
+// Good: Getters don't use "Get" prefix
+user.Name() // not user.GetName()
+user.SetName(n) // Setter keeps "Set"
+
+// Good: For interfaces with single method
+type Reader interface {
+ Read(p []byte) (n int, err error)
+}
+```
+
+**Constants**
+- Use `MixedCaps`: `MaxRetries`, `DefaultTimeout`
+- Or group in const blocks with clear names
+
+### Commentary and Documentation
+
+**Package Comments**
+```go
+// Package regexp implements regular expression search.
+//
+// The syntax of the regular expressions accepted is:
+//
+// regexp:
+// concatenation { '|' concatenation }
+package regexp
+```
+
+**Function Comments**
+```go
+// Compile parses a regular expression and returns, if successful,
+// a Regexp object that can be used to match against text.
+func Compile(str string) (*Regexp, error) {
+```
+
+**Key principles**:
+- Doc comments should be complete sentences
+- Start with the name of the element being described
+- Explain what, why, and any non-obvious behavior
+- Document errors that can be returned
+
+### Error Handling
+
+**Check every error**
+```go
+// Good: Handle errors immediately
+result, err := doSomething()
+if err != nil {
return fmt.Errorf("failed to do something: %w", err)
}
-// Good: Check errors immediately
-result, err := fetchData()
+// Bad: Ignoring errors
+result, _ := doSomething() // Never do this
+```
+
+**Wrap errors with context**
+```go
+// Use %w to wrap errors for programmatic inspection
+if err := os.Remove(file); err != nil {
+ return fmt.Errorf("removing %s: %w", file, err)
+}
+
+// Use %v for simple annotation without inspection
+if err := validate(data); err != nil {
+ return fmt.Errorf("validation failed: %v", err)
+}
+```
+
+**Check error types when needed**
+```go
+if errors.Is(err, os.ErrNotExist) {
+ // Handle file not found
+}
+
+var pathErr *os.PathError
+if errors.As(err, &pathErr) {
+ // Access pathErr.Path, pathErr.Op
+}
+```
+
+**Error handling patterns**
+```go
+// Good: Keep normal path at minimal indentation
+value, err := someOperation()
if err != nil {
return err
}
+// Continue with value...
-// Bad: Ignoring errors
-result, _ := fetchData() // Don't do this
+// Good: Early returns for error conditions
+if user == nil {
+ return errors.New("user cannot be nil")
+}
+if !user.IsActive {
+ return errors.New("user is not active")
+}
+// Continue with valid user...
```
-#### Use context.Context
+### Interfaces
+
+**Design principles**
+- Interfaces define behavior, not data
+- Prefer small, focused interfaces (often single-method)
+- Define interfaces where they're used, not where they're implemented
+- Accept interfaces, return concrete types
+
```go
-// Good: Accept context as first parameter
-func ProcessRequest(ctx context.Context, req *Request) error {
- // Use ctx for cancellation, deadlines, values
- select {
- case <-ctx.Done():
- return ctx.Err()
- case result := <-process(req):
- return result
+// Good: Small, focused interface
+type Reader interface {
+ Read(p []byte) (n int, err error)
+}
+
+// Good: Compose interfaces
+type ReadWriter interface {
+ Reader
+ Writer
+}
+
+// Good: Define interface in consumer package
+// Package needs something that can save data
+type DataSaver interface {
+ Save(data []byte) error
+}
+```
+
+### Concurrency
+
+**Goroutines**
+```go
+// Good: Make it clear when goroutines exit
+func process(ctx context.Context) {
+ done := make(chan bool)
+
+ go func() {
+ defer close(done)
+ // Do work
+ for {
+ select {
+ case <-ctx.Done():
+ return
+ default:
+ doWork()
+ }
+ }
+ }()
+
+ <-done
+}
+```
+
+**Channels**
+```go
+// Good: Specify channel direction in function signatures
+func producer(out chan<- int) {
+ out <- 42
+ close(out)
+}
+
+func consumer(in <-chan int) {
+ for val := range in {
+ fmt.Println(val)
}
}
+
+// Good: Use buffered channels for resource limiting
+requests := make(chan *Request, 100)
```
-#### Prefer Composition Over Inheritance
+**Share by communicating**
```go
-// Good: Embed types for composition
-type Server struct {
- logger *Logger
- cache *Cache
+// Good: Use channels instead of shared memory
+type result struct {
+ value int
+ err error
}
-// Good: Define interfaces
-type DataStore interface {
- Get(key string) ([]byte, error)
- Set(key string, value []byte) error
+resultCh := make(chan result)
+go func() {
+ val, err := compute()
+ resultCh <- result{val, err}
+}()
+
+res := <-resultCh
+if res.err != nil {
+ // handle error
}
```
-#### Keep Functions Small and Focused
+### Context Usage
+
+**Always use context**
+```go
+// Good: Accept context as first parameter
+func FetchUser(ctx context.Context, id string) (*User, error) {
+ req, err := http.NewRequestWithContext(ctx, "GET", url, nil)
+ if err != nil {
+ return nil, err
+ }
+ // ...
+}
+
+// Good: Propagate context through call chains
+func ProcessRequest(ctx context.Context, req *Request) error {
+ user, err := FetchUser(ctx, req.UserID)
+ if err != nil {
+ return err
+ }
+ return SaveData(ctx, user)
+}
+```
+
+### Function Design
+
+**Keep functions small and focused**
```go
// Good: Single responsibility
func validateEmail(email string) error {
@@ -105,36 +319,42 @@ func validateEmail(email string) error {
return nil
}
-// Bad: Doing too much
-func processUserAndSendEmail(user User) error {
- // Validation
- // Database operations
- // Email sending
- // Logging
- // ... (too many responsibilities)
+func createUser(name, email string) (*User, error) {
+ if err := validateEmail(email); err != nil {
+ return nil, err
+ }
+ return &User{Name: name, Email: email}, nil
}
```
-### Naming Conventions
+**Use options pattern for complex configuration**
+```go
+type ServerOption func(*Server)
-#### Variables
-- Use camelCase for local variables: `userCount`, `httpClient`
-- Use short names in small scopes: `i`, `err`, `ok`
-- Use descriptive names in larger scopes: `userRepository`, `configManager`
+func WithTimeout(d time.Duration) ServerOption {
+ return func(s *Server) {
+ s.timeout = d
+ }
+}
-#### Functions and Methods
-- Use camelCase: `getUserByID`, `processRequest`
-- Exported functions start with uppercase: `NewClient`, `ProcessData`
-- Getters don't use "Get" prefix: `user.Name()`, not `user.GetName()`
+func NewServer(addr string, opts ...ServerOption) *Server {
+ s := &Server{addr: addr, timeout: 30 * time.Second}
+ for _, opt := range opts {
+ opt(s)
+ }
+ return s
+}
-#### Constants
-- Use camelCase or uppercase with underscores
-- Exported: `MaxRetries`, `DefaultTimeout`
-- Private: `defaultBufferSize`, `maxConnections`
+// Usage
+srv := NewServer(":8080",
+ WithTimeout(10*time.Second),
+ WithMaxConns(100))
+```
## Testing
### Table-Driven Tests
+
```go
func TestValidateEmail(t *testing.T) {
tests := []struct {
@@ -152,13 +372,19 @@ func TestValidateEmail(t *testing.T) {
email: "userexample.com",
wantErr: true,
},
+ {
+ name: "empty email",
+ email: "",
+ wantErr: true,
+ },
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
err := validateEmail(tt.email)
if (err != nil) != tt.wantErr {
- t.Errorf("validateEmail() error = %v, wantErr %v", err, tt.wantErr)
+ t.Errorf("validateEmail(%q) error = %v, wantErr %v",
+ tt.email, err, tt.wantErr)
}
})
}
@@ -166,49 +392,129 @@ func TestValidateEmail(t *testing.T) {
```
### Test Organization
-- Tests in same package: `package mypackage`
-- Tests in separate package: `package mypackage_test` (black-box testing)
-- Use subtests with `t.Run()` for clarity
-- Mock external dependencies using interfaces
+
+**Black-box vs White-box testing**
+```go
+// White-box: same package, can access internals
+package mypackage
+
+func TestInternalFunction(t *testing.T) { }
+
+// Black-box: separate package, tests public API only
+package mypackage_test
+
+import "myproject/mypackage"
+
+func TestPublicAPI(t *testing.T) { }
+```
+
+**Test helpers**
+```go
+// Mark test helpers with t.Helper()
+func assertEqual(t *testing.T, got, want int) {
+ t.Helper()
+ if got != want {
+ t.Errorf("got %d, want %d", got, want)
+ }
+}
+```
+
+**Use t.Error over t.Fatal**
+```go
+// Good: t.Error allows other tests to continue
+func TestMultipleChecks(t *testing.T) {
+ if got := compute(1); got != 2 {
+ t.Errorf("compute(1) = %d, want 2", got)
+ }
+ if got := compute(2); got != 4 {
+ t.Errorf("compute(2) = %d, want 4", got)
+ }
+}
+
+// Use t.Fatal only when continuing makes no sense
+func TestSetup(t *testing.T) {
+ db, err := setupDatabase()
+ if err != nil {
+ t.Fatalf("setupDatabase() failed: %v", err)
+ }
+ // Can't continue without db
+}
+```
### Test Coverage
-- Aim for meaningful coverage, not 100%
-- Focus on critical paths and edge cases
-- Use `go test -cover ./...` to check coverage
-- Don't test trivial code
-### Running Tests
```bash
-# Run all tests
-go test ./...
-
-# Run with coverage
+# Run tests with coverage
go test -cover ./...
-# Run specific test
-go test -run TestValidateEmail
+# Generate coverage profile
+go test -coverprofile=coverage.out ./...
-# Verbose output
-go test -v ./...
+# View coverage in browser
+go tool cover -html=coverage.out
-# Run tests before committing
-go test ./...
+# Check coverage for specific packages
+go test -cover ./internal/...
+```
+
+**Coverage guidelines**:
+- Aim for meaningful coverage, not arbitrary percentages
+- Focus on critical business logic and edge cases
+- Don't test trivial code (getters, setters)
+- Use coverage to find untested code paths, not as a goal
+
+### Benchmarking
+
+```go
+func BenchmarkCompute(b *testing.B) {
+ input := generateInput()
+
+ b.ResetTimer() // Reset timer after setup
+ for i := 0; i < b.N; i++ {
+ compute(input)
+ }
+}
+
+func BenchmarkComputeParallel(b *testing.B) {
+ input := generateInput()
+
+ b.RunParallel(func(pb *testing.PB) {
+ for pb.Next() {
+ compute(input)
+ }
+ })
+}
+```
+
+```bash
+# Run benchmarks
+go test -bench=. ./...
+
+# With memory allocation stats
+go test -bench=. -benchmem ./...
+
+# CPU profiling
+go test -bench=. -cpuprofile=cpu.prof
+
+# Analyze profile
+go tool pprof cpu.prof
```
## Common Patterns
### Configuration Management
+
```go
type Config struct {
- Port int `json:"port"`
- Timeout time.Duration `json:"timeout"`
- LogLevel string `json:"log_level"`
+ Port int `json:"port" yaml:"port"`
+ Timeout time.Duration `json:"timeout" yaml:"timeout"`
+ LogLevel string `json:"log_level" yaml:"log_level"`
}
func LoadConfig(path string) (*Config, error) {
data, err := os.ReadFile(path)
if err != nil {
- return nil, fmt.Errorf("reading config: %w", err)
+ return nil, fmt.Errorf("reading config file: %w", err)
}
var cfg Config
@@ -221,164 +527,89 @@ func LoadConfig(path string) (*Config, error) {
```
### Graceful Shutdown
+
```go
func main() {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
+ // Listen for interrupt signals
sigChan := make(chan os.Signal, 1)
signal.Notify(sigChan, os.Interrupt, syscall.SIGTERM)
go func() {
<-sigChan
- log.Println("Shutting down...")
+ log.Println("Shutdown signal received, cleaning up...")
cancel()
}()
if err := run(ctx); err != nil {
- log.Fatal(err)
+ log.Fatalf("Application error: %v", err)
}
}
+
+func run(ctx context.Context) error {
+ // Application logic with context
+ <-ctx.Done()
+ return cleanup()
+}
```
### HTTP Client with Timeout
+
```go
+// Good: Configure timeouts and connection pooling
client := &http.Client{
Timeout: 10 * time.Second,
Transport: &http.Transport{
MaxIdleConns: 100,
MaxIdleConnsPerHost: 10,
IdleConnTimeout: 90 * time.Second,
+ TLSHandshakeTimeout: 10 * time.Second,
},
}
-```
-## Nix Integration
+// Use context for per-request timeout
+ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
+defer cancel()
-### Building
-```bash
-# Build a Go tool package
-nix build .#<package-name>
-
-# Install to profile
-nix profile install .#<package-name>
-
-# Check what would be built
-nix build .#<package-name> --dry-run
-```
-
-### Testing in Nix
-```bash
-# Enter dev shell
-nix develop
-
-# Run tests
-cd tools/<tool-name>
-go test ./...
-```
-
-### Package Definition
-```nix
-# In pkgs/<package-name>/default.nix
-{ lib, buildGoModule }:
-
-buildGoModule {
- pname = "tool-name";
- version = "1.0.0";
-
- src = ../../tools/tool-name;
-
- vendorHash = "sha256-...";
-
- meta = with lib; {
- description = "Tool description";
- license = licenses.mit;
- };
+req, err := http.NewRequestWithContext(ctx, "GET", url, nil)
+if err != nil {
+ return err
}
+
+resp, err := client.Do(req)
+if err != nil {
+ return err
+}
+defer resp.Body.Close()
```
-## Common Commands
+### Resource Management with sync.Pool
-### Formatting and Linting
-```bash
-# Format code
-gofmt -w .
-
-# Organize imports
-goimports -w .
-
-# Vet code
-go vet ./...
-
-# Run linter
-golangci-lint run
-
-# Fix linter issues automatically
-golangci-lint run --fix
-```
-
-### Dependency Management
-```bash
-# Add dependency
-go get github.com/pkg/errors
-
-# Update dependencies
-go get -u ./...
-
-# Tidy modules
-go mod tidy
-
-# Verify dependencies
-go mod verify
-
-# Vendor dependencies
-go mod vendor
-```
-
-### Build and Run
-```bash
-# Build
-go build -o bin/myapp cmd/myapp/main.go
-
-# Run
-go run cmd/myapp/main.go
-
-# Install
-go install ./cmd/myapp
-```
-
-## Performance
-
-### Benchmarking
```go
-func BenchmarkFunction(b *testing.B) {
- for i := 0; i < b.N; i++ {
- doSomething()
- }
+// Good: Reuse expensive objects
+var bufferPool = sync.Pool{
+ New: func() interface{} {
+ return new(bytes.Buffer)
+ },
+}
+
+func processData(data []byte) error {
+ buf := bufferPool.Get().(*bytes.Buffer)
+ defer bufferPool.Put(buf)
+ buf.Reset()
+
+ // Use buffer
+ buf.Write(data)
+ return process(buf)
}
```
-### Profiling
-```bash
-# CPU profiling
-go test -cpuprofile cpu.prof -bench .
-
-# Memory profiling
-go test -memprofile mem.prof -bench .
-
-# Analyze profile
-go tool pprof cpu.prof
-```
-
-### Optimization Tips
-- Use sync.Pool for frequently allocated objects
-- Prefer slices over arrays when size varies
-- Use buffered channels appropriately
-- Avoid premature optimization - profile first!
-
## Common Pitfalls to Avoid
### ❌ Goroutine Leaks
+
```go
// Bad: Goroutine never exits
go func() {
@@ -401,35 +632,212 @@ go func(ctx context.Context) {
```
### ❌ Race Conditions
+
```go
// Bad: Concurrent map access
var cache = make(map[string]string)
-// Good: Use sync.Map or mutex
+func get(key string) string {
+ return cache[key] // Race!
+}
+
+// Good: Use sync.Map for concurrent access
var cache sync.Map
+
+func get(key string) string {
+ val, _ := cache.Load(key)
+ if val != nil {
+ return val.(string)
+ }
+ return ""
+}
+
+// Or use mutex for complex operations
+type Cache struct {
+ mu sync.RWMutex
+ data map[string]string
+}
+
+func (c *Cache) Get(key string) string {
+ c.mu.RLock()
+ defer c.mu.RUnlock()
+ return c.data[key]
+}
```
### ❌ Defer in Loops
+
```go
-// Bad: Defers accumulate
+// Bad: Defers accumulate until function exits
for _, file := range files {
f, _ := os.Open(file)
defer f.Close() // Won't close until function exits!
}
-// Good: Close immediately
+// Good: Close immediately or use function wrapper
for _, file := range files {
func() {
- f, _ := os.Open(file)
+ f, err := os.Open(file)
+ if err != nil {
+ log.Printf("error opening %s: %v", file, err)
+ return
+ }
defer f.Close()
+ processFile(f)
}()
}
```
+### ❌ Pointer to Loop Variable
+
+```go
+// Bad: All goroutines will use the last value
+for _, val := range values {
+ go func() {
+ process(val) // Bug: val is the loop variable!
+ }()
+}
+
+// Good: Pass value as parameter
+for _, val := range values {
+ go func(v string) {
+ process(v)
+ }(val)
+}
+```
+
+### ❌ Nil Channel Operations
+
+```go
+// Bad: Sending/receiving on nil channel blocks forever
+var ch chan int
+ch <- 1 // Blocks forever!
+
+// Good: Initialize channels
+ch := make(chan int)
+ch <- 1
+```
+
+## Tooling
+
+### Essential Commands
+
+```bash
+# Format code
+gofmt -w .
+goimports -w .
+
+# Vet code for common mistakes
+go vet ./...
+
+# Run tests
+go test ./...
+go test -v ./... # Verbose
+go test -cover ./... # With coverage
+go test -race ./... # Race detector
+
+# Dependency management
+go mod tidy # Clean up dependencies
+go mod verify # Verify dependencies
+go get -u ./... # Update dependencies
+
+# Build
+go build -o bin/myapp ./cmd/myapp
+go install ./cmd/myapp
+
+# Cross-compilation
+GOOS=linux GOARCH=amd64 go build
+GOOS=darwin GOARCH=arm64 go build
+```
+
+### Linting
+
+```bash
+# Install golangci-lint
+go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest
+
+# Run all linters
+golangci-lint run
+
+# Auto-fix issues
+golangci-lint run --fix
+
+# Run specific linters
+golangci-lint run --enable=gosec,exhaustive
+```
+
+### Profiling
+
+```bash
+# CPU profiling
+go test -cpuprofile cpu.prof -bench .
+go tool pprof cpu.prof
+
+# Memory profiling
+go test -memprofile mem.prof -bench .
+go tool pprof mem.prof
+
+# Block profiling (find blocking goroutines)
+go test -blockprofile block.prof -bench .
+go tool pprof block.prof
+
+# Analyze in browser
+go tool pprof -http=:8080 cpu.prof
+```
+
+## Performance Best Practices
+
+1. **Profile before optimizing**: Use `pprof` to find actual bottlenecks
+2. **Use appropriate data structures**:
+ - Slices for sequential access
+ - Maps for key-value lookups
+ - Channels for communication
+3. **Preallocate when size is known**:
+ ```go
+ items := make([]Item, 0, expectedSize)
+ ```
+4. **Reuse objects with sync.Pool**: For frequently allocated objects
+5. **Avoid unnecessary allocations**:
+ - Use string builders instead of concatenation
+ - Reuse buffers when possible
+6. **Benchmark critical paths**: Write benchmarks for performance-critical code
+7. **Use buffered channels appropriately**: Avoid blocking on channel operations
+
+## Nix Integration
+
+> **Note**: If you detect Nix usage in this project (presence of `flake.nix`, `default.nix`, or Nix package definitions), refer to `nix-integration.md` in this skill directory for detailed Nix-specific build instructions, package definitions, and integration patterns.
+
+Quick check for Nix:
+- Look for `flake.nix`, `default.nix`, or `shell.nix` in project root
+- Check for `/pkgs` or `/tools` directories with Nix definitions
+- User mentions Nix, NixOS, or buildGoModule
+
## Resources
-- Effective Go: https://go.dev/doc/effective_go
-- Go Code Review Comments: https://github.com/golang/go/wiki/CodeReviewComments
-- Standard library documentation: https://pkg.go.dev/std
+### Official Documentation
+- [Effective Go](https://go.dev/doc/effective_go) - Core Go programming guide
+- [Go Code Review Comments](https://go.dev/wiki/CodeReviewComments) - Common code review issues
+- [Go Standard Library](https://pkg.go.dev/std) - Documentation and examples
+- [Google Go Style Guide](https://google.github.io/styleguide/go/) - Production best practices
-Remember: Write clear, simple, idiomatic Go code. When in doubt, check the standard library for examples.
+### Tools
+- [gofmt](https://pkg.go.dev/cmd/gofmt) - Code formatter
+- [goimports](https://pkg.go.dev/golang.org/x/tools/cmd/goimports) - Import management
+- [golangci-lint](https://golangci-lint.run/) - Comprehensive linter
+- [gopls](https://github.com/golang/tools/tree/master/gopls) - Language server
+
+### Learning Resources
+- [Go by Example](https://gobyexample.com/) - Practical examples
+- [Go Playground](https://go.dev/play/) - Try Go in browser
+- [Uber Go Style Guide](https://github.com/uber-go/guide/blob/master/style.md) - Additional patterns
+
+---
+
+**Remember**: Write clear, simple, idiomatic Go code. When in doubt, consult Effective Go and the standard library for guidance. The Go community values simplicity, readability, and maintainability above cleverness.
+
+**Sources**:
+- [Effective Go](https://go.dev/doc/effective_go)
+- [Go Code Review Comments](https://go.dev/wiki/CodeReviewComments)
+- [Google Go Style Guide](https://google.github.io/styleguide/go/)
+- [Go Best Practices 2025](https://www.bacancytechnology.com/blog/go-best-practices)
+- [Go Coding Standards](https://leapcell.medium.com/go-coding-official-standards-and-best-practices-5e84f4dbc8ad)