Generated banner for Golang Grpc
Generated logo for Golang Grpc

Golang Grpc

Samuel BertheCommunityCreated by samberDistributed by Samuel Berthe

  • Skill
  • Tool integration
  • Software development
  • MIT
  • No issues found

From the author

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.
— the description by samber

AutoGineer summary

Written by AutoGineer from the skill’s own files, not by the author

This skill provides a comprehensive framework for building robust gRPC microservices in Go. It covers the full lifecycle of gRPC development, including protobuf organization, server and client implementation patterns, error handling, security configurations, and testing strategies. It emphasizes production-grade practices such as graceful shutdowns, interceptor usage, proper status code mapping, and connection management to ensure reliable and maintainable distributed systems.

What it does

  • Provides best practices for organizing protobuf files and generating Go code.
  • Offers patterns for implementing gRPC servers with health checks and interceptors.
  • Defines standard approaches for client-side connection management and load balancing.
  • Guides developers on mapping Go errors to appropriate gRPC status codes.
  • Includes techniques for testing gRPC services using in-memory bufconn connections.
  • Outlines security configurations including TLS/mTLS and production hardening.

When to use it

  • When designing or implementing a new gRPC service in Go.
  • When auditing existing gRPC codebases for correctness and operability.
  • When debugging gRPC communication issues or performance bottlenecks.
  • When setting up testing suites for gRPC clients and servers.

Advantages

  • Promotes consistent, production-ready gRPC architecture.
  • Reduces common pitfalls like connection leaks and improper error handling.
  • Facilitates easier testing of complex gRPC stacks without network overhead.
  • Encourages clean separation between transport layers and business logic.

Usage examples

“How do I implement a graceful shutdown for my gRPC server in Go?”
The agent provides a pattern using GracefulStop() with a timeout fallback to Stop() to ensure in-flight requests are handled correctly.
“Show me how to test my gRPC service using bufconn.”
The agent generates a test setup using bufconn to create an in-memory connection, allowing full stack testing without network dependencies.
“What is the best way to handle errors in my gRPC service?”
The agent explains how to use status.Errorf with specific codes like NotFound or InvalidArgument instead of returning raw errors.
“How should I organize my proto files for a Go project?”
The agent recommends a domain-based, versioned directory structure (e.g., proto/user/v1/) and the use of Request/Response wrapper messages.

Install

  1. Download the skill as a ZIP:

    https://autogineer.com/agent-skills/samber/golang-grpc/download
  2. Unzip it into the cross-client skills folder of your project:

    mkdir -p .agents/skills && unzip golang-grpc.zip -d .agents/skills

Documentation by samber

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.

Dependencies:

  • protoc: brew install protobuf

  • protoc-gen-go: go install google.golang.org/protobuf/cmd/protoc-gen-go@latest

  • protoc-gen-go-grpc: go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest

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 — refer to library documentation and code examples for more information:

  • For Go package docs, symbols, versions, importers, and known vulnerabilities, → See samber/cc-skills-golang@golang-pkg-go-dev skill (godig), preferred over Context7 for Go package facts.

  • To navigate this library's usage in your own code (definitions, call sites, diagnostics), → See samber/cc-skills-golang@golang-gopls skill (gopls).

  • Context7 remains a fallback for docs not indexed on pkg.go.dev.

Quick Reference

ConcernPackage / Tool
Service definitionprotoc or buf with .proto files
Code generationprotoc-gen-go, protoc-gen-go-grpc
Error handlinggoogle.golang.org/grpc/status with codes
Rich error detailsgoogle.golang.org/genproto/googleapis/rpc/errdetails
Interceptorsgrpc.ChainUnaryInterceptor, grpc.ChainStreamInterceptor
Middleware ecosystemgithub.com/grpc-ecosystem/go-grpc-middleware
Testinggoogle.golang.org/grpc/test/bufconn
TLS / mTLSgoogle.golang.org/grpc/credentials
Health checksgoogle.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
InvalidArgumentMalformed input (missing field, bad format)
NotFoundEntity does not exist
AlreadyExistsCreate failed, entity exists
PermissionDeniedCaller lacks permission
UnauthenticatedMissing or invalid token
FailedPreconditionSystem not in required state
ResourceExhaustedRate limit or quota exceeded
UnavailableTransient issue, safe to retry
InternalUnexpected bug
DeadlineExceededTimeout
// ✗ 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.TimePing interval for idle connections30s
keepalive.ServerParameters.TimeoutPing ack timeout10s
grpc.MaxRecvMsgSizeOverride 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 errorBecomes 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 errorsWrong 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

Files

evals/evals.json

[
  {
    "id": 1,
    "name": "raw-error-vs-status-error",
    "description": "Tests that gRPC handlers return status.Errorf with specific codes, not raw errors",
    "prompt": "Write a Go gRPC handler for GetUser that looks up a user from a repository. If the user is not found, return an appropriate error. If the repository returns an unexpected error, return that too.",
    "trap": "Without the skill, the model may return fmt.Errorf or raw errors, which become codes.Unknown. The skill requires status.Errorf with specific codes.",
    "assertions": [
      {
        "id": "1.1",
        "text": "Uses status.Errorf (or status.Error) for the not-found case with codes.NotFound"
      },
      {
        "id": "1.2",
        "text": "Uses status.Errorf with codes.Internal for unexpected errors"
      },
      {
        "id": "1.3",
        "text": "Does NOT return a raw fmt.Errorf or errors.New as the gRPC error"
      },
      {
        "id": "1.4",
        "text": "Imports google.golang.org/grpc/status and google.golang.org/grpc/codes"
      },
      {
        "id": "1.5",
        "text": "Does NOT leak internal error details in the user-facing gRPC message for Internal errors"
      }
    ]
  },
  {
    "id": 2,
    "name": "wrapper-messages-not-bare-types",
    "description": "Tests that proto RPCs use Request/Response wrapper messages, not bare types",
    "prompt": "Write a proto3 service definition for a product catalog with RPCs: GetProduct (takes product ID, returns product), DeleteProduct (takes product ID, returns nothing), SearchProducts (takes search query string, returns list of products).",
    "trap": "Without the skill, the model may use bare types like string or google.protobuf.Empty. The skill requires Request/Response wrappers for backward-compatible evolution.",
    "assertions": [
      {
        "id": "2.1",
        "text": "Uses GetProductRequest/GetProductResponse wrapper messages, not bare string or Product"
      },
      {
        "id": "2.2",
        "text": "Uses DeleteProductRequest/DeleteProductResponse wrapper messages, not bare string or google.protobuf.Empty"
      },
      {
        "id": "2.3",
        "text": "Uses SearchProductsRequest/SearchProductsResponse wrapper messages"
      },
      {
        "id": "2.4",
        "text": "Each wrapper message has properly named fields (not just a single unnamed field)"
      },
      { "id": "2.5", "text": "Includes go_package option in the proto file" }
    ]
  },
  {
    "id": 3,
    "name": "proto-directory-organization",
    "description": "Tests proper proto file organization by domain with versioned directories",
    "prompt": "I'm building a microservice with user management and order management. How should I organize my proto files? Show the directory layout and an example buf.gen.yaml.",
    "trap": "Without the skill, the model may put all protos in a flat directory or skip versioning. The skill requires domain-based organization with v1 directories.",
    "assertions": [
      {
        "id": "3.1",
        "text": "Organizes proto files by domain (user/, order/ or similar domain grouping)"
      },
      {
        "id": "3.2",
        "text": "Includes version directories (v1/ under each domain)"
      },
      {
        "id": "3.3",
        "text": "Separates message definitions from service definitions (e.g. user.proto vs user_service.proto)"
      },
      {
        "id": "3.4",
        "text": "Includes a shared/ or common/ directory for shared messages"
      },
      {
        "id": "3.5",
        "text": "Shows buf.gen.yaml with go and go-grpc plugins configured"
      }
    ]
  },
  {
    "id": 4,
    "name": "graceful-stop-with-timeout-fallback",
    "description": "Tests proper gRPC server shutdown: GracefulStop with timeout fallback to Stop",
    "prompt": "Write Go code for a gRPC server that handles shutdown signals properly. The server should try to drain in-flight requests before stopping.",
    "trap": "Without the skill, the model may only call srv.Stop() or only srv.GracefulStop() without a timeout fallback. The skill requires GracefulStop with a timeout fallback to Stop.",
    "assertions": [
      {
        "id": "4.1",
        "text": "Calls srv.GracefulStop() first to drain in-flight RPCs"
      },
      {
        "id": "4.2",
        "text": "Has a timeout mechanism that falls back to srv.Stop() if GracefulStop takes too long"
      },
      {
        "id": "4.3",
        "text": "Uses a select statement or timer for the timeout"
      },
      {
        "id": "4.4",
        "text": "Listens for OS signals (SIGINT, SIGTERM or os.Interrupt)"
      },
      { "id": "4.5", "text": "The timeout is reasonable (5-30 seconds)" }
    ]
  },
  {
    "id": 5,
    "name": "health-check-service-registration",
    "description": "Tests that gRPC servers register the health check service for Kubernetes readiness probes",
    "prompt": "Write a production-ready gRPC server setup in Go. Register a UserService and make it deployable to Kubernetes.",
    "trap": "Without the skill, the model may not register the health check service. The skill says health check is required for Kubernetes probes.",
    "assertions": [
      {
        "id": "5.1",
        "text": "Registers grpc_health_v1.RegisterHealthServer (or equivalent health check service)"
      },
      {
        "id": "5.2",
        "text": "Uses health.NewServer() to create the health service"
      },
      {
        "id": "5.3",
        "text": "Registers interceptors using grpc.ChainUnaryInterceptor"
      },
      {
        "id": "5.4",
        "text": "Does NOT enable reflection (or explicitly disables it for production)"
      },
      { "id": "5.5", "text": "Includes graceful shutdown handling" }
    ]
  },
  {
    "id": 6,
    "name": "client-connection-reuse-and-deadlines",
    "description": "Tests that gRPC clients reuse connections and set deadlines on every call",
    "prompt": "Write a Go gRPC client that calls a UserService. Create the client and make a GetUser call.",
    "trap": "Without the skill, the model may create a new connection per request or not set a deadline. The skill requires connection reuse and context.WithTimeout on every call.",
    "assertions": [
      {
        "id": "6.1",
        "text": "Creates the connection once and reuses it (not creating a new connection per call)"
      },
      {
        "id": "6.2",
        "text": "Uses context.WithTimeout (or context.WithDeadline) for the RPC call"
      },
      {
        "id": "6.3",
        "text": "Uses grpc.NewClient (not the deprecated grpc.Dial)"
      },
      {
        "id": "6.4",
        "text": "Includes transport credentials (TLS or explicitly insecure for dev)"
      },
      {
        "id": "6.5",
        "text": "Properly defers cancel() from context.WithTimeout"
      }
    ]
  },
  {
    "id": 7,
    "name": "bufconn-testing-pattern",
    "description": "Tests that gRPC tests use bufconn for in-memory connections, not real network",
    "prompt": "Write a test for a Go gRPC UserService that verifies GetUser returns the correct user and that requesting a non-existent user returns the proper error code.",
    "trap": "Without the skill, the model may start a real TCP server for testing. The skill requires bufconn for in-memory connections.",
    "assertions": [
      { "id": "7.1", "text": "Uses bufconn.Listen for an in-memory listener" },
      {
        "id": "7.2",
        "text": "Uses grpc.WithContextDialer with the bufconn dialer"
      },
      {
        "id": "7.3",
        "text": "Verifies the gRPC status code for the not-found case using status.FromError"
      },
      {
        "id": "7.4",
        "text": "Checks that the error code is codes.NotFound specifically"
      },
      {
        "id": "7.5",
        "text": "Uses t.Cleanup or defer for cleaning up the server and connection"
      }
    ]
  },
  {
    "id": 8,
    "name": "error-code-selection-judgment",
    "description": "Tests correct gRPC error code selection across different scenarios",
    "prompt": "Write a Go gRPC handler for CreateOrder that validates the request has a non-empty user_id and at least one item, checks the user exists, checks inventory availability, and creates the order. Return appropriate gRPC errors for each failure case.",
    "trap": "Without the skill, the model may use codes.Internal for everything or pick wrong codes. The skill has a specific mapping table.",
    "assertions": [
      {
        "id": "8.1",
        "text": "Uses codes.InvalidArgument for missing user_id or empty items list"
      },
      { "id": "8.2", "text": "Uses codes.NotFound for user not found" },
      {
        "id": "8.3",
        "text": "Uses codes.FailedPrecondition for insufficient inventory (system not in required state)"
      },
      {
        "id": "8.4",
        "text": "Uses codes.Internal only for truly unexpected errors"
      },
      {
        "id": "8.5",
        "text": "Does NOT use codes.Unknown for any handled error case"
      }
    ]
  },
  {
    "id": 9,
    "name": "unimplemented-server-embedding",
    "description": "Tests that gRPC server implementations embed UnimplementedXxxServer, not UnsafeXxxServer",
    "prompt": "Write a Go implementation of a gRPC server for an OrderService with CreateOrder and GetOrder RPCs. Show the struct definition and method signatures.",
    "trap": "Without the skill, the model may embed UnsafeXxxServer or not embed any base server. The skill requires UnimplementedXxxServer for forward compatibility.",
    "assertions": [
      {
        "id": "9.1",
        "text": "Embeds UnimplementedOrderServiceServer in the server struct"
      },
      { "id": "9.2", "text": "Does NOT embed UnsafeOrderServiceServer" },
      {
        "id": "9.3",
        "text": "Implements the service methods with correct signatures (context, request pointer, response pointer and error return)"
      },
      {
        "id": "9.4",
        "text": "Uses status.Errorf for error returns in the handler methods"
      },
      {
        "id": "9.5",
        "text": "Registers the server with pb.RegisterOrderServiceServer"
      }
    ]
  },
  {
    "id": 10,
    "name": "client-load-balancing-and-retry",
    "description": "Tests knowledge of gRPC client-side load balancing and retry configuration",
    "prompt": "I'm deploying a Go gRPC client in Kubernetes. The server has multiple replicas behind a headless service. How do I configure the client to distribute requests across all replicas and retry on transient failures?",
    "trap": "Without the skill, the model may suggest a separate load balancer or basic round-robin without the dns:/// scheme. The skill shows the specific configuration.",
    "assertions": [
      {
        "id": "10.1",
        "text": "Uses the dns:/// scheme for service discovery with headless Kubernetes services"
      },
      {
        "id": "10.2",
        "text": "Configures round_robin load balancing policy via service config"
      },
      {
        "id": "10.3",
        "text": "Configures retry policy in the service config with retryableStatusCodes containing UNAVAILABLE"
      },
      {
        "id": "10.4",
        "text": "Sets maxAttempts, initialBackoff, maxBackoff in the retry policy"
      },
      {
        "id": "10.5",
        "text": "Does NOT suggest creating a new connection per request for load distribution"
      }
    ]
  },
  {
    "id": 11,
    "name": "streaming-over-large-messages",
    "description": "Tests preference for streaming over large single messages",
    "prompt": "I need to send a result set of 50,000 records from a gRPC server to a client. Each record is about 1KB. What's the best approach?",
    "trap": "Without the skill, the model may suggest increasing MaxRecvMsgSize and sending all records in one response. The skill recommends streaming to avoid per-message size limits and memory pressure.",
    "assertions": [
      {
        "id": "11.1",
        "text": "Recommends server streaming RPC to send records incrementally"
      },
      {
        "id": "11.2",
        "text": "Explains why a single large message is problematic (size limits, memory pressure)"
      },
      {
        "id": "11.3",
        "text": "Shows the server streaming pattern with stream.Send in a loop"
      },
      {
        "id": "11.4",
        "text": "Client reads with stream.Recv in a loop, checking for io.EOF"
      },
      {
        "id": "11.5",
        "text": "Does NOT just increase MaxRecvMsgSize as the primary solution"
      }
    ]
  }
]

Requirements and compatibility

Written for: Claude Code, OpenAI Codex

Works with: Claude Code, Claude apps, OpenAI Codex, GitHub Copilot, Cursor, Gemini CLI, Kiro, OpenCode, Goose, Amp, Roo Code, JetBrains Junie, Factory Droid, Devin (ex-Windsurf), Mistral Vibe, OpenHands, Letta

“Works with” means the tool reads the open Agent Skills format; a skill written for one tool may use features others lack.

Needs: Declares allowed tools, Claude Code frontmatter

The author says: Designed for Claude Code, Codex or similar harness, and for projects using Golang.

Security

No issues found by our static scan· No issues found

A static scan is not a guarantee. Skills are third-party content: review a skill before you let an agent use it.

Attribution and licence

Created by
samber
Publisher
Samuel Berthe (Community)
Distributed by
Samuel Berthe
Source
samber/cc-skills-golang · skills/golang-grpc · commit bb1d43d
Licence
MIT License (redistribution allowed)
  • LICENSE (added by AutoGineer from LICENSE)
  • ATTRIBUTION.md (written by AutoGineer)
Synced

AutoGineer is not affiliated with or endorsed by samber or Samuel Berthe.

Download ZIP

The ZIP holds the skill’s files, its licence files and an ATTRIBUTION.md. Download golang-grpc.zip