Install
$ agentstack add skill-brpaz-agent-skills-golang-development ✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ● Network access Used
- ● Filesystem access Used
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
Verified badge
Passed review? Show it. Paste this badge into your README, it links to the public security report.
Reliability & compatibility
Declared compatibility
Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.
We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.
How agent discovery & health will work →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.gopercmd//subdirectory - Keep
pkg/minimal. Place application code ininternal/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
// ❌ 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, notstructs,interfaces)
Error Handling
Basic Pattern
// ✅ 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
// ✅ 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
// 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
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
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:
go tool pprof http://localhost:6060/debug/pprof/profile
Common Pitfalls
Range Loop Variable Capture
// ❌ 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
// ❌ 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
// ❌ 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
// ❌ 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
// ❌ 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
// 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
// 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
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
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
// 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
# 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
.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.Contextas 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 fmtbefore committing code - no exceptions. - ALWAYS run tests with
-raceflag to catch race conditions. - NEVER ignore errors - at minimum, log them; never use
_for error returns. - NEVER use
panicfor 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.Builderfor 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, notstructs;logger, notinterfaces. - Avoid stuttering in names -
user.Repository, notuser.UserRepository.
Quick Reference
Minimal HTTP Server
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
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
# Initialize module
go mod init github.com/myorg/myapp
# Add dependency
go get github.com/pkg/errors
# Remove unused dependencies
go mod tidy
Resources
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
// 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
- Source: brpaz/agent-skills
- License: MIT
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet, be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.