From go-dev
Write production-ready Go backends, CLIs, and APIs following modern best practices from top tier tech companies. Use this skill when creating or reviewing Go code for (1) backend services and APIs, (2) command-line tools, (3) code requiring proper error handling, concurrency, or testing patterns, (4) any Go development requiring adherence to established style guidelines. Includes comprehensive linting configuration and detailed style guide.
npx claudepluginhub marsolab/skills --plugin go-devThis skill uses the workspace's default tool permissions.
Write Go code that is readable, maintainable, and production-ready using
Generates design tokens/docs from CSS/Tailwind/styled-components codebases, audits visual consistency across 10 dimensions, detects AI slop in UI.
Records polished WebM UI demo videos of web apps using Playwright with cursor overlay, natural pacing, and three-phase scripting. Activates for demo, walkthrough, screen recording, or tutorial requests.
Delivers idiomatic Kotlin patterns for null safety, immutability, sealed classes, coroutines, Flows, extensions, DSL builders, and Gradle DSL. Use when writing, reviewing, refactoring, or designing Kotlin code.
Write Go code that is readable, maintainable, and production-ready using battle-tested patterns from major production codebases.
Always use Context7 MCP to fetch the latest documentation.
func TestProcess(t *testing.T) {
type testCase struct {
// Fields for the test case.
}
tests := map[string]testCase{
"name": {
// Test case fields.
},
// More test cases.
}
for name, tc := range tests {
t.Run(name, func(t *testing.T) {
// Test code.
})
}
}
func TestIntegration(t *testing.T) {
if os.Getenv("INTEGRATION_TESTS") == "" {
t.Skip("skipping integration tests")
}
// Test code.
}
t.Helper() so failure line numbers point to the
actual test.func TestHelper(t *testing.T) {
t.Helper()
// Test code.
}
Define interfaces at consumption site, not implementation:
// GOOD: Consumer defines what it needs
package storage
type Store interface {
Get(key string) ([]byte, error)
}
// BAD: Implementation forces interface on consumers
package postgres
type PostgresStore interface { ... }
Interface size:
Accept interfaces, return concrete types:
// GOOD
func Process(r io.Reader) (*Result, error)
// BAD: Forces caller to deal with interface
func Process(r io.Reader) (io.Reader, error)
Decision tree:
%w wrapping%v wrapping// Handle completely
if err != nil {
log.Printf("retrying with defaults: %v", err)
return useDefaults(), nil
}
// Caller needs access (use %w)
if err != nil {
return fmt.Errorf("connect to database: %w", err)
}
// Hide details (use %v)
if err != nil {
return fmt.Errorf("service unavailable: %v", err)
}
Error string format:
"operation: %w"Leave concurrency to the caller unless:
// GOOD: Synchronous by default
func Fetch(url string) (*Response, error)
// Caller decides concurrency
go fetch(url)
// BAD: Forces async on everyone
func FetchAsync(url string) <-chan *Response
Before launching a goroutine, know when it will stop:
// GOOD: Clear lifecycle
ctx, cancel := context.WithTimeout(ctx, 30*time.Second)
defer cancel()
go func() {
for {
select {
case <-ctx.Done():
return // goroutine stops here
case work := <-ch:
process(work)
}
}
}()
Always use context when:
// GOOD
func Query(ctx context.Context, sql string) (*Rows, error)
// BAD: Can't be cancelled
func Query(sql string) (*Rows, error)
1. Project structure:
myservice/
├── cmd/
│ └── server/
│ └── main.go # Binary entrypoint
├── internal/
│ ├── handler/ # HTTP handlers
│ │ ├── handler.go
│ │ └── handler_test.go
│ ├── service/ # Business logic
│ │ ├── service.go
│ │ └── service_test.go
│ └── storage/ # Data layer
│ ├── postgres.go
│ └── postgres_test.go
├── go.mod
├── go.sum
├── Makefile
└── .golangci.yml
2. Initialize project:
mkdir -p myservice/{cmd/server,internal/{handler,service,storage}}
cd myservice
go mod init github.com/yourorg/myservice
# Setup linting
/path/to/scripts/setup_golangci_lint.sh .
3. Main.go pattern:
package main
import (
"context"
"flag"
"log"
"net/http"
"os"
"os/signal"
"time"
)
func main() {
// Flags only in main
addr := flag.String("addr", ":8080", "listen address")
timeout := flag.Duration("timeout", 30*time.Second, "request timeout")
flag.Parse()
// Initialize dependencies
srv := &http.Server{
Addr: *addr,
Handler: setupRoutes(),
ReadTimeout: *timeout,
WriteTimeout: *timeout,
}
// Graceful shutdown
go func() {
sigint := make(chan os.Signal, 1)
signal.Notify(sigint, os.Interrupt)
<-sigint
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
if err := srv.Shutdown(ctx); err != nil {
log.Printf("shutdown error: %v", err)
}
}()
log.Printf("listening on %s", *addr)
if err := srv.ListenAndServe(); err != http.ErrServerClosed {
log.Fatalf("server error: %v", err)
}
}
4. Handler pattern:
package handler
import (
"encoding/json"
"net/http"
)
type Handler struct {
service Service
}
func New(svc Service) *Handler {
return &Handler{service: svc}
}
func (h *Handler) HandleGet(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
// Extract params
id := r.URL.Query().Get("id")
if id == "" {
http.Error(w, "missing id", http.StatusBadRequest)
return
}
// Call service
result, err := h.service.Get(ctx, id)
if err != nil {
// Log and return appropriate status
http.Error(w, "internal error", http.StatusInternalServerError)
return
}
// Respond
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(result)
}
1. Structure:
mycli/
├── main.go # Flag parsing and dispatch
├── internal/
│ └── command/
│ ├── run.go # Command implementations
│ └── run_test.go
├── go.mod
└── .golangci.yml
2. Main.go with subcommands:
package main
import (
"flag"
"fmt"
"os"
)
func main() {
if len(os.Args) < 2 {
fmt.Fprintf(os.Stderr, "usage: %s <command> [flags]\n", os.Args[0])
os.Exit(1)
}
switch os.Args[1] {
case "process":
processCmd := flag.NewFlagSet("process", flag.ExitOnError)
input := processCmd.String("input", "", "input file")
processCmd.Parse(os.Args[2:])
if err := runProcess(*input); err != nil {
fmt.Fprintf(os.Stderr, "error: %v\n", err)
os.Exit(1)
}
default:
fmt.Fprintf(os.Stderr, "unknown command: %s\n", os.Args[1])
os.Exit(1)
}
}
1. Table-driven test pattern:
func TestProcess(t *testing.T) {
tests := []struct {
name string
input string
want string
wantErr bool
}{
{
name: "valid input",
input: "hello",
want: "HELLO",
wantErr: false,
},
{
name: "empty input",
input: "",
want: "",
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := Process(tt.input)
if (err != nil) != tt.wantErr {
t.Errorf("Process() error = %v, wantErr %v", err, tt.wantErr)
return
}
if got != tt.want {
t.Errorf("Process() = %v, want %v", got, tt.want)
}
})
}
}
2. Test helper pattern:
func TestHandler(t *testing.T) {
h := setupHandler(t) // Helper creates handler
req := newRequest(t, "GET", "/api/test") // Helper creates request
rr := httptest.NewRecorder()
h.ServeHTTP(rr, req)
assertStatus(t, rr.Code, http.StatusOK) // Helper asserts
assertBody(t, rr.Body.String(), "expected")
}
func setupHandler(t *testing.T) http.Handler {
t.Helper() // Marks this as test helper
// Setup code
}
For comprehensive coverage of all Go idioms, patterns, and best practices:
Read references/go-styleguide.md for:
Run the setup script:
scripts/setup_golangci_lint.sh /path/to/your/project
This configures comprehensive linting including:
Common commands:
# Run all linters
golangci-lint run
# Auto-fix issues
golangci-lint run --fix
# Lint specific paths
golangci-lint run ./internal/...
Naming:
obj.Owner() not obj.GetOwner()URL or url, never Url)Error handling:
fmt.Errorf("operation: %w", err)Concurrency:
sync.WaitGroup for coordinationStructure:
Testing:
t.Run() for subtestst.Helper() in helpersgot X, want YCritical pitfalls:
make() first