skills/golang-grpc/SKILL.md
Provides gRPC usage guidelines, protobuf organization, and production-ready patterns for Golang microservices. Use when implementing, reviewing, or debugging gRPC servers/clients, writing proto files, setting up interceptors, handling gRPC errors with status codes, configuring TLS/mTLS, testing with bufconn, or working with streaming RPCs.
npx skillsauth add samber/cc-skills-golang golang-grpcInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Persona: You are a Go distributed systems engineer. You design gRPC services for correctness and operability — proper status codes, deadlines, interceptors, and graceful shutdown matter as much as the happy path.
Modes:
Dependencies:
brew install protobufgo install google.golang.org/protobuf/cmd/protoc-gen-go@latestgo install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latestTreat gRPC as a pure transport layer — keep it separate from business logic. The official Go implementation is google.golang.org/grpc.
This skill is not exhaustive — refer to library documentation and code examples for more information:
samber/cc-skills-golang@golang-pkg-go-dev skill (godig), preferred over Context7 for Go package facts.samber/cc-skills-golang@golang-gopls skill (gopls).| Concern | Package / Tool |
| --- | --- |
| Service definition | protoc or buf with .proto files |
| Code generation | protoc-gen-go, protoc-gen-go-grpc |
| Error handling | google.golang.org/grpc/status with codes |
| Rich error details | google.golang.org/genproto/googleapis/rpc/errdetails |
| Interceptors | grpc.ChainUnaryInterceptor, grpc.ChainStreamInterceptor |
| Middleware ecosystem | github.com/grpc-ecosystem/go-grpc-middleware |
| Testing | google.golang.org/grpc/test/bufconn |
| TLS / mTLS | google.golang.org/grpc/credentials |
| Health checks | google.golang.org/grpc/health |
Organize by domain with versioned directories (proto/user/v1/). Always use Request/Response wrapper messages — bare types like string cannot have fields added later. Generate with buf generate or protoc.
Proto & code generation reference
grpc_health_v1) — Kubernetes probes need it to determine readinessGracefulStop() with a timeout fallback to Stop() — drains in-flight RPCs while preventing hangssrv := grpc.NewServer(
grpc.ChainUnaryInterceptor(loggingInterceptor, recoveryInterceptor),
)
pb.RegisterUserServiceServer(srv, svc)
healthpb.RegisterHealthServer(srv, health.NewServer())
go srv.Serve(lis)
// On shutdown signal:
stopped := make(chan struct{})
go func() { srv.GracefulStop(); close(stopped) }()
select {
case <-stopped:
case <-time.After(15 * time.Second):
srv.Stop()
}
func loggingInterceptor(ctx context.Context, req any, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (any, error) {
start := time.Now()
resp, err := handler(ctx, req)
log.Printf("method=%s duration=%s code=%s", info.FullMethod, time.Since(start), status.Code(err))
return resp, err
}
context.WithTimeout) — without one, a slow upstream hangs goroutines indefinitelyround_robin with headless Kubernetes services via dns:/// schememetadata.NewOutgoingContextconn, err := grpc.NewClient("dns:///user-service:50051",
grpc.WithTransportCredentials(creds),
grpc.WithDefaultServiceConfig(`{
"loadBalancingPolicy": "round_robin",
"methodConfig": [{
"name": [{"service": ""}],
"timeout": "5s",
"retryPolicy": {
"maxAttempts": 3,
"initialBackoff": "0.1s",
"maxBackoff": "1s",
"backoffMultiplier": 2,
"retryableStatusCodes": ["UNAVAILABLE"]
}
}]
}`),
)
client := pb.NewUserServiceClient(conn)
Always return gRPC errors using status.Error with a specific code — a raw error becomes codes.Unknown, telling the client nothing actionable. Clients use codes to decide retry vs fail-fast vs degrade.
| Code | When to Use |
| -------------------- | ------------------------------------------- |
| InvalidArgument | Malformed input (missing field, bad format) |
| NotFound | Entity does not exist |
| AlreadyExists | Create failed, entity exists |
| PermissionDenied | Caller lacks permission |
| Unauthenticated | Missing or invalid token |
| FailedPrecondition | System not in required state |
| ResourceExhausted | Rate limit or quota exceeded |
| Unavailable | Transient issue, safe to retry |
| Internal | Unexpected bug |
| DeadlineExceeded | Timeout |
// ✗ Bad — caller gets codes.Unknown, can't decide whether to retry
return nil, fmt.Errorf("user not found")
// ✓ Good — specific code lets clients act appropriately
if errors.Is(err, ErrNotFound) {
return nil, status.Errorf(codes.NotFound, "user %q not found", req.UserId)
}
return nil, status.Errorf(codes.Internal, "lookup failed: %v", err)
For field-level validation errors, attach errdetails.BadRequest via status.WithDetails.
| Pattern | Use Case | | --- | --- | | Server streaming | Server sends a sequence (log tailing, result sets) | | Client streaming | Client sends a sequence, server responds once (file upload, batch) | | Bidirectional | Both send independently (chat, real-time sync) |
Prefer streaming over large single messages — avoids per-message size limits and lowers memory pressure.
func (s *server) ListUsers(req *pb.ListUsersRequest, stream pb.UserService_ListUsersServer) error {
for _, u := range users {
if err := stream.Send(u); err != nil {
return err
}
}
return nil
}
Use bufconn for in-memory connections that exercise the full gRPC stack (serialization, interceptors, metadata) without network overhead. Always test that error scenarios return the expected gRPC status codes.
Testing patterns and examples
credentials.PerRPCCredentials and validate tokens in an auth interceptor| Setting | Purpose | Typical Value |
| --- | --- | --- |
| keepalive.ServerParameters.Time | Ping interval for idle connections | 30s |
| keepalive.ServerParameters.Timeout | Ping ack timeout | 10s |
| grpc.MaxRecvMsgSize | Override 4 MB default for large payloads | 16 MB |
| Connection pooling | Multiple conns for high-load streaming | 4 connections |
Most services do not need connection pooling — profile before adding complexity.
| Mistake | Fix |
| --- | --- |
| Returning raw error | Becomes codes.Unknown — client can't decide whether to retry. Use status.Errorf with a specific code |
| No deadline on client calls | Slow upstream hangs indefinitely. Always context.WithTimeout |
| New connection per request | Wastes TCP/TLS handshakes. Create once, reuse — HTTP/2 multiplexes RPCs |
| Reflection enabled in production | Lets attackers enumerate every method. Enable only in dev/staging |
| codes.Internal for all errors | Wrong codes break client retry logic. Unavailable triggers retry; InvalidArgument does not |
| Bare types as RPC arguments | Can't add fields to string. Wrapper messages allow backwards-compatible evolution |
| Missing health check service | Kubernetes can't determine readiness, kills pods during deployments |
| Ignoring context cancellation | Long operations continue after caller gave up. Check ctx.Err() |
samber/cc-skills-golang@golang-context skill for deadline and cancellation patternssamber/cc-skills-golang@golang-error-handling skill for gRPC error to Go error mappingsamber/cc-skills-golang@golang-observability skill for gRPC interceptors (logging, tracing, metrics)samber/cc-skills-golang@golang-testing skill for gRPC testing with bufconndevelopment
Golang refactoring — safe, at-scale restructuring of existing Go code: a coverage-adaptive safety net, behavior-preserving transforms (gopls Rename/Extract, `gofmt -r`, `gopatch`), the Fowler catalog mapped to Go, breaking import cycles, and small stacked PRs. Apply when a function or type has grown too large, a code smell blocks a feature, or the user asks to refactor Go code — also for renaming at scale, extracting functions or interfaces, moving code between packages, or planning a multi-step refactor. Target styles owned elsewhere → See `samber/cc-skills-golang@golang-naming` (renames), `samber/cc-skills-golang@golang-project-layout` (splits), `samber/cc-skills-golang@golang-modernize` (idioms), `samber/cc-skills-golang@golang-code-style` (control flow), `samber/cc-skills-golang@golang-design-patterns` (patterns/DI).
tools
Golang semantic code intelligence via `gopls`, the official Go language server — go-to-definition, find references, call/implementation hierarchy, workspace symbol search, package API discovery, diagnostics, safe rename, refactors (extract/inline/fill/rewrite code actions), formatting, and generated tests. Reaches an agent via gopls's own MCP server (`go_*` tools), Claude Code's native `LSP` tool, or the `gopls` CLI. Use when navigating or refactoring Go code — jumping to a definition, finding call sites before a rename, understanding a file's or package's dependencies, running diagnostics after an edit, or extracting/inlining/renaming. Not for the published ecosystem — packages not in your `go.mod`, versions, licenses, importers — → See `samber/cc-skills-golang@golang-pkg-go-dev` skill (`godig`). Not for a whole-tree vulnerability audit → See `samber/cc-skills-golang@golang-security` skill (`govulncheck`).
tools
Golang package and module lookup via `godig`, a pkg.go.dev API client (CLI + MCP server). Use for any Go/Golang library's documentation, API signatures, symbols, usage examples, which versions exist, licenses, whether a dependency has CVEs, or who imports a package — prefer this over Context7 for any Go package or module. Read-only, no auth. Not for upgrading dependencies (→ See `samber/cc-skills-golang@golang-dependency-management` skill), choosing a library (→ See `samber/cc-skills-golang@golang-popular-libraries` skill), or local symbols and an already-used dependency's resolved source, call sites, and generic instantiations (→ See `samber/cc-skills-golang@golang-gopls` skill).
development
Golang skills orchestrator — always active on any Golang coding, review, debug, or setup task. Reads the task context and loads the most relevant skills from samber/cc-skills-golang, often multiple at once: writing a gRPC service loads golang-grpc + golang-testing + golang-error-handling; debugging a panic loads golang-troubleshooting + golang-safety; auditing security loads golang-security + golang-lint + golang-safety. Also: disambiguates competing clusters when two skills seem to overlap (performance vs benchmark vs troubleshooting, samber/lo vs mo vs ro, DI cluster, safety vs security), and configures the project's agent-config file (CLAUDE.md, AGENTS.md, GEMINI.md, Cursor rules, or Copilot instructions) to force-trigger skills in a project (/golang-how-to configure).