gRPC gives typed service contracts and efficient streaming, but compatibility depends on field-number discipline, request presence, status mapping, deadline propagation, retry policy, metadata limits, and flow-controlled stream ownership. This pattern constrains schema evolution, deadline handling, and retry safety; it records status, streaming, and gateway decisions and provides a contract and interoperability review. It complements REST and GraphQL patterns by focusing on generated binary contracts, HTTP/2 streams, service-to-service deadlines, and Protobuf wire compatibility.
Suggested path map
Pathrule places each piece on the matching path, so your assistant only sees it where it belongs. This is the scoping you get on import; you can adjust it in your workspace.
/ workspace root
review-grpc-contract
proto/
Evolve Protobuf schemas without changing wire meaning
Field presence is a business decision
src/
grpc/
Propagate deadlines and cancellation through every downstream call
Retry only idempotent RPC outcomes under an explicit policy
Status codes carry stable public failure semantics
Streaming handlers own backpressure and half-close behavior
Rules
3
Evolve Protobuf schemas without changing wire meaning/protohighstrictNever reuse field numbers, reserve removed identities, add compatible fields, and version semantic breaks deliberately.
1
Generated code hides the wire format, but deployed clients, stored messages, and queued payloads may retain old field numbers for years. Reusing one makes old bytes decode as a different concept.
2
3
- Reserve the names and numbers of removed fields and enum values so future authors cannot assign them accidentally.
4
- Add fields with safe absence behavior and avoid changing a field's type, repetition, or semantic meaning under the same number.
5
- Keep enum zero as a deliberate unspecified value and make receivers handle values newer than their generated code.
6
- Introduce a new message, method, or service version for incompatible required semantics instead of relying on coordinated fleet deployment.
7
8
See /tests/contract for the adjacent decision or procedure that completes this constraint.
Propagate deadlines and cancellation through every downstream call/src/grpchighstrictRequire a bounded deadline, derive child budgets, stop work on cancellation, and avoid committing after the caller can no longer receive the result.
1
Without a deadline, an RPC can occupy threads, streams, connections, and database work indefinitely. A service that ignores cancellation also wastes capacity after its caller has already timed out.
2
3
- Set client deadlines from the product operation budget and reject unbounded internal calls at the service boundary.
4
- Propagate the remaining deadline to downstream RPC, database, and external work while reserving time for local cleanup and response serialization.
5
- Observe cancellation in handlers and streaming loops, release resources promptly, and do not continue optional work merely because a goroutine or promise is still running.
6
- Design commit points so cancellation before commit aborts and cancellation after commit returns or reconciles a stable operation outcome.
7
8
See /src/grpc for the adjacent decision or procedure that completes this constraint.
Retry only idempotent RPC outcomes under an explicit policy/src/grpchighstrictClassify method idempotency and retryable transport statuses, apply bounded backoff, and preserve operation identity.
1
A transport failure or unavailable status does not always prove the server did no work. Retrying a mutation can duplicate the effect unless the application recognizes the same logical request.
2
3
- Mark read-only or idempotent methods explicitly in client policy and leave state-changing methods without automatic retry unless they use an idempotency key.
4
- Retry only selected transient statuses before the overall deadline and stop after bounded attempts with jitter.
5
- Do not retry validation, authentication, authorization, failed precondition, conflict, or unsupported-operation outcomes.
6
- Include a stable operation identity in state-changing requests and make the server return the prior result after ambiguous client retry.
7
8
See /tests/contract for the adjacent decision or procedure that completes this constraint.
Memories
3
Status codes carry stable public failure semantics/src/grpcMap domain and transport failures to the narrowest gRPC status and attach bounded structured details for machines.
1
Returning UNKNOWN or INTERNAL for every expected failure prevents clients from deciding whether to correct input, refresh identity, resolve conflict, or retry later.
2
3
- Use invalid-argument for malformed values, failed-precondition for unmet state, not-found for absent authorized resources, and permission outcomes without revealing hidden resources.
4
- Reserve unavailable and resource-exhausted for transient service or capacity conditions callers may handle under policy.
5
- Attach structured error details only when clients have a contract to consume them and keep metadata within bounded size.
6
- Log internal causes with correlation identity while returning a stable public message that excludes secrets, stack traces, SQL, and dependency internals.
7
8
See /proto for the rule or workflow that puts this decision into practice.
Streaming handlers own backpressure and half-close behavior/src/grpcRead and write streams incrementally, bound application queues, and define cancellation and partial-result semantics.
1
HTTP/2 provides transport flow control, but application code can defeat it by reading an entire stream into memory or producing into an unbounded queue before writes complete.
2
3
- Process incoming messages incrementally with limits on count, bytes, concurrency, and per-message cost.
4
- Wait for outbound write readiness or completion rather than appending indefinitely to an application buffer.
5
- Define what client half-close means, when the server may finish, and whether partial results are valid after an error.
6
- Stop producer and consumer work together on cancellation or stream failure so one side does not leak after the other exits.
7
8
See /tests/contract for the rule or workflow that puts this decision into practice.
Field presence is a business decision/protoUse presence-aware fields or explicit wrappers where omitted, default, cleared, and unchanged have different meanings.
1
Protobuf scalar defaults can make an absent value look like an explicit zero or empty string. Patch and configuration APIs often need to distinguish those states.
2
3
- Model optional input with presence when omission means leave unchanged and an explicit default means set or clear.
4
- Use a field mask or command-specific patch message for partial updates rather than interpreting every default as missing.
5
- Keep output defaults semantically valid so older clients can tolerate newly absent or unknown data.
6
- Test generated clients in every supported language because presence and JSON mapping ergonomics differ even when the wire behavior is compatible.
7
8
See /src/grpc for the rule or workflow that puts this decision into practice.
Skills
1
review-grpc-contract/rootCheck Protobuf compatibility, generated clients, deadlines, statuses, retries, metadata, and streams across supported languages.
1
---
2
name: review-grpc-contract
3
description: Review a gRPC service or Protobuf change before publishing generated artifacts or deploying servers.
4
---
5
6
# Review Grpc Contract
7
8
Run this procedure when the affected surface changes, before the result is promoted to production. Record evidence for every step instead of accepting a plausible-looking result.
9
10
1. Run schema compatibility checks against the released descriptor set and inspect every changed number, name, type, enum, oneof, and presence rule.
11
2. Generate supported clients and compile representative callers, including older clients against the new server and new clients against the old server.
12
3. Exercise deadline expiry, cancellation, authentication, authorization, validation, conflict, capacity, and dependency failure and verify status semantics.
13
4. Test retry policy with ambiguous state-changing outcomes and stable operation identity; prove no method retries beyond the caller's deadline.
14
5. For streams, test slow readers, slow writers, half-close, large messages, cancellation, server restart, and bounded memory under sustained flow.
15
16
## Exit criteria
17
18
The change is complete only when the expected behavior, failure behavior, and rollback path have all been exercised with representative data. Preserve the evidence with the change so the next operator can repeat the same checks.
Why this pattern
AI agents often reuse a removed field number, omit deadlines, retry non-idempotent methods, map every failure to UNKNOWN, or read an unbounded stream without cancellation.
Built for Platform and backend teams building internal RPC, public gRPC, or streaming service contracts.
Keeps your assistant from:
Reinterpreting old serialized data through a reused field number
Letting an upstream call outlive the caller's deadline
Duplicating a state-changing RPC through transparent retry
Buffering an unbounded stream faster than the consumer can process it