Cc-skills-golang golang-grpc

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.

install
source · Clone the upstream repo
git clone https://github.com/samber/cc-skills-golang
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/samber/cc-skills-golang "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/golang-grpc" ~/.claude/skills/samber-cc-skills-golang-golang-grpc && rm -rf "$T"
manifest: skills/golang-grpc/SKILL.md
source content

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:

  • Build mode — implementing a new gRPC server or client from scratch.
  • Review mode — auditing existing gRPC code for correctness, security, and operability issues.

Go gRPC Best Practices

Treat 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. Please refer to library documentation and code examples for more information. Context7 can help as a discoverability platform.

Quick Reference

ConcernPackage / 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

Proto File Organization

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

Server Implementation

  • Implement health check service (
    grpc_health_v1
    ) — Kubernetes probes need it to determine readiness
  • Use interceptors for cross-cutting concerns (logging, auth, recovery) — keeps business logic clean
  • Use
    GracefulStop()
    with a timeout fallback to
    Stop()
    — drains in-flight RPCs while preventing hangs
  • Disable reflection in production — it exposes your full API surface
srv := 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()
}

Interceptor Pattern

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
}

Client Implementation

  • Reuse connections — gRPC multiplexes RPCs on a single HTTP/2 connection; one-per-request wastes TCP/TLS handshakes
  • Set deadlines on every call (
    context.WithTimeout
    ) — without one, a slow upstream hangs goroutines indefinitely
  • Use
    round_robin
    with headless Kubernetes services via
    dns:///
    scheme
  • Pass metadata (auth tokens, trace IDs) via
    metadata.NewOutgoingContext
conn, 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)

Error Handling

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.

CodeWhen 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
.

Streaming

PatternUse Case
Server streamingServer sends a sequence (log tailing, result sets)
Client streamingClient sends a sequence, server responds once (file upload, batch)
BidirectionalBoth 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
}

Testing

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

Security

  • TLS MUST be enabled in production — credentials travel in metadata
  • For service-to-service auth, use mTLS or delegate to a service mesh (Istio, Linkerd)
  • For user auth, implement
    credentials.PerRPCCredentials
    and validate tokens in an auth interceptor
  • Reflection SHOULD be disabled in production to prevent API discovery

Performance

SettingPurposeTypical Value
keepalive.ServerParameters.Time
Ping interval for idle connections30s
keepalive.ServerParameters.Timeout
Ping ack timeout10s
grpc.MaxRecvMsgSize
Override 4 MB default for large payloads16 MB
Connection poolingMultiple conns for high-load streaming4 connections

Most services do not need connection pooling — profile before adding complexity.

Common Mistakes

MistakeFix
Returning raw
error
Becomes
codes.Unknown
— client can't decide whether to retry. Use
status.Errorf
with a specific code
No deadline on client callsSlow upstream hangs indefinitely. Always
context.WithTimeout
New connection per requestWastes TCP/TLS handshakes. Create once, reuse — HTTP/2 multiplexes RPCs
Reflection enabled in productionLets 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 argumentsCan't add fields to
string
. Wrapper messages allow backwards-compatible evolution
Missing health check serviceKubernetes can't determine readiness, kills pods during deployments
Ignoring context cancellationLong operations continue after caller gave up. Check
ctx.Err()

Cross-References

  • → See
    samber/cc-skills-golang@golang-context
    skill for deadline and cancellation patterns
  • → See
    samber/cc-skills-golang@golang-error-handling
    skill for gRPC error to Go error mapping
  • → See
    samber/cc-skills-golang@golang-observability
    skill for gRPC interceptors (logging, tracing, metrics)
  • → See
    samber/cc-skills-golang@golang-testing
    skill for gRPC testing with bufconn