grpc-services
Skill8 starsChanged 9 days agoMITView on GitHub
Builds and reviews gRPC services and .proto contracts - versioned packages, field numbering and reserved fields, buf lint and buf breaking in CI, status codes and rich error details, deadlines and propagation, retries via service config, streaming choices, load balancing over HTTP/2, health checks, reflection, interceptors, TLS/mTLS, and browser access via gRPC-Web, Connect or JSON transcoding. Use when writing or changing .proto files, implementing gRPC servers or clients, calls hang or all traffic hits one pod, or exposing gRPC to browsers. Not for choosing between REST, GraphQL and gRPC (see api-design).
1 other project uses a version of this.
Adopted by
Most recently taken up by
- MaheshAwasare/claude-skills-protheir own version, first seen May 4, 2026
Other versions1 project uses a version of this.
Word for word · 0
None found.
Edited · 0
None found.
Wrote their own version · 1
- MaheshAwasare/claude-skills-pro★ 1same name
How matches are found. Word for word: the exact same text. Edited: shares passages with this file (“99% same” means 99% of its five-word phrases also appear here; 10% or more counts). Their own version: similar meaning, or, weakest, the same name. We can’t tell which project had it first. Projects are counted among the 200 most-starred candidates, and mentions are links to the repository we found, up to 50 per site, so both counts are a floor.
In this repository60 other files it gives its agents.
- api-designSkill
- auth-implementationSkill
- background-jobsSkill
- building-agentsSkill
- caching-strategiesSkill
- ci-cd-pipelinesSkill
- cloud-cost-optimizationSkill
- codebase-onboardingSkill
- code-review-changesSkill
- concurrency-patternsSkill
- database-migrationsSkill
- database-schema-designSkill
- database-testingSkill
- data-pipelinesSkill
- data-privacy-retentionSkill
- dependency-upgradesSkill
- design-docsSkill
- design-systemsSkill
- dockerfilesSkill
- domain-driven-designSkill
- event-driven-architectureSkill
- file-storage-uploadsSkill
- frontend-securitySkill
- git-workflowSkill
- graphql-apisSkill
- incident-responseSkill
- infrastructure-as-codeSkill
- internationalizationSkill
- kubernetes-deploymentsSkill
- large-scale-migrationsSkill
- legacy-refactoringSkill
- llm-evalsSkill
- llm-integrationSkill
- mcp-serversSkill
- micro-frontendsSkill
- ml-experimentsSkill
- monorepo-managementSkill
- nosql-designSkill
- observabilitySkill
- orm-patternsSkill
- performance-profilingSkill
- postgres-operationsSkill
- rag-systemsSkill
- react-patternsSkill
- realtime-apisSkill
- resilience-patternsSkill
- root-cause-debuggingSkill
- search-integrationSkill
- security-auditSkill
- seo-and-ssrSkill
- sql-optimizationSkill
- system-designSkill
- test-strategySkill
- threat-modelingSkill
- typescript-patternsSkill
- unit-testingSkill
- verify-before-doneSkill
- web-accessibilitySkill
- web-performanceSkill
- writing-skillsSkill
The file
Show its rules and text
We split this skill into the rules it gives an agent
13 rules. About 0 are common, 9 are distinctive, and 4 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈9
Distinctive · estimate
This author's own choices. Worth a look.
4
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 16
All traffic goes to one backend pod, or connections drop behind a proxy.
- Very specific: line 19
Not for: choosing a protocol (the api-design skill has the comparison table and a status-code table in references/graphql-and-grpc.md), or …
- Very specific: line 122
Run buf lint and buf breaking as required CI checks on every change under proto/.
- Distinctive: line 128
Every client call sets a deadline.
- Distinctive: line 139
Check for cancellation in long handlers and stop work when the client has gone.
- Very specific: line 169
Use the right code: INVALID_ARGUMENT for bad input, NOT_FOUND, ALREADY_EXISTS, FAILED_PRECONDITION for wrong state, ABORTED for concurrency …
- Distinctive: line 196
Every proxy on the path must support HTTP/2 end to end, or gRPC fails in confusing ways.
- Distinctive: line 216
Use OpenTelemetry's gRPC instrumentation for traces and metrics instead of hand-rolled timing.
- Distinctive: line 219
Use TLS everywhere outside local development.
- Very specific: line 234
Test deadline behavior: a slow fake dependency must produce DEADLINE_EXCEEDED, not a hang.
- Distinctive: line 235
Test error mapping: each domain error yields the expected code and details.
- Distinctive: line 247
Huge messages: instead of streaming or object storage: memory spikes and size-limit errors.
- Distinctive: line 254
Every client call has a deadline.
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
gRPC Services
The .proto file is a binary wire contract: field numbers and types can never change meaning once a client has shipped. Protect the contract with tooling, put a deadline on every call, and remember that HTTP/2 connections are long-lived, which changes how load balancing works.
When to use
- Writing or changing
.protofiles, or adding a new gRPC service. - Implementing a gRPC server or client in any language.
- Calls hang, time out in chains, or retries cause duplicates.
- All traffic goes to one backend pod, or connections drop behind a proxy.
- Browsers or third parties need to call a gRPC service.
Not for: choosing a protocol (the api-design skill has the comparison table and a status-code table in references/graphql-and-grpc.md), or message streaming between services through a broker (see event-driven-architecture).
Workflow
- Design the proto with the rules below, in a dedicated
proto/directory with one package per API version. - Lint and check compatibility:
buf lintandbuf breakinglocally and in CI. - Generate code with
buf generate(orprotoc) from a checked-in config. Don't hand-edit generated files. - Implement the server with interceptors for recovery, tracing, logging, metrics and auth, and a single place that maps domain errors to status codes.
- Implement clients with deadlines on every call, retries in service config, and an explicit load-balancing choice.
- Add the standard health service and wire it to readiness probes.
- Test with an in-process server, then manually with
grpcurl. - Instrument per-method latency, status codes and in-flight streams (see the observability skill).
Proto design
syntax = "proto3";
package acme.orders.v1;
import "google/protobuf/field_mask.proto";
import "google/protobuf/timestamp.proto";
service OrderService {
rpc GetOrder(GetOrderRequest) returns (GetOrderResponse);
rpc ListOrders(ListOrdersRequest) returns (ListOrdersResponse);
rpc UpdateOrder(UpdateOrderRequest) returns (UpdateOrderResponse);
}
message Order {
reserved 5; // removed field: number never reused
reserved "legacy_status"; // and neither is its name
string id = 1;
string customer_id = 2;
OrderStatus status = 3;
google.protobuf.Timestamp created_at = 4;
optional string coupon_code = 6; // presence matters: unset vs ""
}
enum OrderStatus {
ORDER_STATUS_UNSPECIFIED = 0; // zero value has no business meaning
ORDER_STATUS_PENDING = 1;
ORDER_STATUS_SHIPPED = 2;
}
message GetOrderRequest { string id = 1; }
message GetOrderResponse { Order order = 1; }
message ListOrdersRequest {
int32 page_size = 1;
string page_token = 2;
}
message ListOrdersResponse {
repeated Order orders = 1;
string next_page_token = 2;
}
message UpdateOrderRequest {
Order order = 1;
google.protobuf.FieldMask update_mask = 2;
}
message UpdateOrderResponse { Order order = 1; }Rules:
| Rule | Why |
Package includes a major version (acme.orders.v1) | Breaking changes go into v2, served side by side until clients migrate |
| Each RPC has its own request and response message | You can add fields to one RPC without affecting others |
| Never change a field's number or type | The wire format identifies fields by number; old clients would misread data |
reserved every removed field number and name | Prevents accidental reuse that corrupts old clients' data |
Enum zero value is <ENUM>_UNSPECIFIED | Proto3 defaults to 0; it must mean "not set" |
| Enum values prefixed with the enum name | Enum values share the package scope in C++ and some other languages |
optional (or wrapper types) where unset differs from zero/empty | Proto3 scalars otherwise can't express "not provided" |
page_size / page_token / next_page_token for lists | Standard pagination; cap page_size on the server |
FieldMask for partial updates | Distinguishes "set to empty" from "don't touch" |
Well-known types for time (Timestamp, Duration) | Consistent across languages |
Google's API Improvement Proposals (aip.dev) are the most complete style guide; buf lint enforces a common subset.
Contract checks with buf
A minimal buf.yaml at the repo root (buf v2 config format; check the buf docs for your version):
version: v2
modules:
- path: proto
lint:
use:
- STANDARD
breaking:
use:
- FILEbuf lint
buf breaking --against '.git#branch=main'
buf generateRun buf lint and buf breaking as required CI checks on every change under proto/. A failing buf breaking means "make a new version", not "add an exception", unless the API owner explicitly accepts the break.
For request validation, declare rules in the proto with a validation library (for example protovalidate) instead of hand-written checks in every handler.
Deadlines
Every client call sets a deadline. A call without one can wait forever and pile up resources across a call chain.
| Language | Setting a deadline |
| Go | ctx, cancel := context.WithTimeout(ctx, 2*time.Second); defer cancel(); client.GetOrder(ctx, req) |
| Java | stub.withDeadlineAfter(2, TimeUnit.SECONDS).getOrder(req) |
| Python | stub.GetOrder(req, timeout=2.0) |
| C# | client.GetOrderAsync(req, deadline: DateTime.UtcNow.AddSeconds(2)) |
Node (@grpc/grpc-js) | client.getOrder(req, { deadline: Date.now() + 2000 }, callback) |
- Propagate the incoming deadline: on the server, pass the request's context (Go
ctx, C#context.CancellationToken, Java's currentContext) to downstream calls so they inherit the remaining time. - Check for cancellation in long handlers and stop work when the client has gone.
- Pick deadlines from the dependency's latency SLO, not a round number. See the resilience-patterns skill.
Retries
- Configure retries in the service config, not in hand-written loops, and only for idempotent methods:
{
"methodConfig": [{
"name": [{ "service": "acme.orders.v1.OrderService", "method": "GetOrder" }],
"timeout": "2s",
"retryPolicy": {
"maxAttempts": 3,
"initialBackoff": "0.1s",
"maxBackoff": "1s",
"backoffMultiplier": 2,
"retryableStatusCodes": ["UNAVAILABLE"]
}
}]
}- Retry
UNAVAILABLEby default. RetryDEADLINE_EXCEEDEDorRESOURCE_EXHAUSTEDonly for idempotent methods and with backoff. - For non-idempotent methods (create, charge), add an idempotency key field to the request and dedupe on the server before enabling any retry.
- Retry at one layer only. If a service mesh also retries, turn one of them off.
Errors
- Map domain errors to status codes in one place (an error-mapping function or server interceptor). Never let handlers return
UNKNOWNor leak internal error strings. - Use the right code:
INVALID_ARGUMENTfor bad input,NOT_FOUND,ALREADY_EXISTS,FAILED_PRECONDITIONfor wrong state,ABORTEDfor concurrency conflicts,PERMISSION_DENIEDvsUNAUTHENTICATED,UNAVAILABLEfor transient failures. The full table with retry guidance is in the api-design skill'sreferences/graphql-and-grpc.md. - Attach structured details with
google.rpc.Statusdetails:BadRequest(field violations),ErrorInfo(stablereasonanddomain),RetryInfo(retry delay). Clients branch on these, not on message text.
Choosing a call type
| Call type | Use when | Watch out for |
| Unary | Request/response; most RPCs | — |
| Server streaming | Server sends many results or live updates (tailing, progress) | Long-lived streams need keepalive, reconnect and resume logic |
| Client streaming | Client uploads many items or chunks | Server must bound total size and time |
| Bidirectional streaming | Interactive sessions (chat, collaborative editing) | Hardest to load-balance, test and observe; use only when needed |
- Message size limits apply per message, and defaults differ by implementation (many default to a few MB on receive). Check your library's defaults. For large data, stream chunks or upload to object storage and pass a reference (see the file-storage-uploads skill).
- Streams pin a client to one backend for their whole life. Prefer unary calls unless streaming is truly needed.
Load balancing and connections
gRPC multiplexes many calls over one long-lived HTTP/2 connection. Connection-level (L4) load balancing therefore sends all of a client's calls to one backend. A plain Kubernetes ClusterIP Service behaves this way.
Pick one:
- Client-side balancing: resolve all backends (e.g. a headless Kubernetes Service with a
dns:///target) and use theround_robinpolicy via service config:{"loadBalancingConfig": [{"round_robin": {}}]}. - L7 proxy or service mesh that balances per request (Envoy, Linkerd, Istio, or a cloud load balancer with HTTP/2 and gRPC support).
- Server
MaxConnectionAge(where supported) so clients reconnect periodically and spread out after scale-ups.
Also:
- Every proxy on the path must support HTTP/2 end to end, or gRPC fails in confusing ways.
- Align client keepalive pings with server keepalive enforcement. Pinging more often than the server allows gets the connection closed (
ENHANCE_YOUR_CALM/ "too many pings").
Health, reflection and tooling
- Implement the standard
grpc.health.v1.Healthservice and set the status toSERVINGonly when dependencies needed to serve are ready. Kubernetes can probe it natively withgrpcprobes (stable since Kubernetes 1.27), or usegrpc_health_probe. - Server reflection lets tools discover services without proto files. Enable it for internal and development environments; consider disabling it on public endpoints.
- Manual testing with
grpcurl:
grpcurl -plaintext localhost:50051 list
grpcurl -plaintext localhost:50051 describe acme.orders.v1.OrderService
grpcurl -plaintext -d '{"id": "ord_123"}' localhost:50051 acme.orders.v1.OrderService/GetOrderDrop -plaintext for TLS endpoints. Never point manual write calls at production without explicit confirmation from the user.
Interceptors, auth and transport security
- Server interceptor order (outermost first): panic/exception recovery, tracing, logging, metrics, authentication, authorization, validation.
- Use OpenTelemetry's gRPC instrumentation for traces and metrics instead of hand-rolled timing.
- Send credentials as metadata (
authorization: Bearer <token>) via per-RPC credentials. Most implementations refuse to send per-RPC credentials over insecure channels, which is intentional. - Authorize per method (and per resource) in the server, not only at the edge. See the auth-implementation skill for service-to-service identity.
- Use TLS everywhere outside local development. Use mTLS for service-to-service traffic, either directly or through a service mesh. Insecure credentials are for local development only.
Browsers and REST clients
| Option | How |
| gRPC-Web | Browser client library plus a proxy (e.g. Envoy) or a server with built-in gRPC-Web support |
| Connect protocol | Connect libraries serve gRPC, gRPC-Web and a simple HTTP/JSON protocol from the same handlers |
| JSON transcoding | Map RPCs to REST routes with google.api.http annotations, served by a gateway (e.g. grpc-gateway, Envoy's transcoder, ASP.NET Core JSON transcoding) |
Renaming a field keeps the binary format but breaks JSON transcoding and JSON clients. Treat renames as breaking when JSON is in play.
Testing
- Unit-test handlers with an in-process server: Go
bufconn, JavaInProcessServerBuilder/InProcessChannelBuilder, .NETWebApplicationFactory, Python a server on a local port. - Test deadline behavior: a slow fake dependency must produce
DEADLINE_EXCEEDED, not a hang. - Test error mapping: each domain error yields the expected code and details.
buf breakingis your contract test for wire compatibility.
Anti-patterns
- No deadlines: hung calls pile up across services.
- Reusing a removed field number: old clients silently misread data.
- Shared request messages across RPCs: one change ripples through unrelated methods.
- Business meaning on enum value 0: unset values look like a real state.
- L4 load balancing: one pod takes all traffic while others sit idle.
- Retrying non-idempotent methods: duplicate orders or charges.
- Returning
UNKNOWN/INTERNALfor everything: clients can't tell bad input from outages. - Huge messages instead of streaming or object storage: memory spikes and size-limit errors.
- Skipping
buf breakingbecause "we only added a field": renumbering and type changes slip in.
Done when
- Protos are versioned by package, linted, and
buf breakingruns as a required CI check. - Removed fields are reserved by number and name; enums start with
_UNSPECIFIED = 0. - Every client call has a deadline; servers propagate deadlines downstream.
- Retries are configured in service config for idempotent methods only.
- Domain errors map to correct status codes with structured details, in one place.
- Load balancing is per request (client-side or L7), verified by watching traffic spread across backends.
- The health service is implemented and used by readiness probes; TLS or mTLS is on outside local development.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

