Markdown Editor

gRPC Service Specification Template (Protocol Buffers)

gRPC service spec: .proto contract, package versioning, field compatibility, google.rpc errors, deadlines, retries, streaming

Архітектура та системний дизайнСтандартgRPC and Protocol Buffers documentationgrpcservicespec

Як використовувати: Use it before generating code for a new gRPC service or major version. Fill the .proto excerpt, package versioning and field numbering rules first, since field numbers are permanent. The main pitfall is calls without deadlines and retries without idempotency.

Перегляд

gRPC Service Specification: [package.ServiceName]

Specification of one gRPC service and its Protocol Buffers contract: purpose, .proto definitions, package and versioning, message and field conventions, compatibility rules, errors using the google.rpc status codes, deadlines, retries, streaming, authentication, observability and load balancing. Based on the public gRPC and Protocol Buffers documentation (grpc.io, protobuf.dev); examples use proto3 syntax, and notes cover Protobuf Editions (latest released edition: 2024). Replace every [bracketed] value and every row marked Example. Worked examples use a payments ledger service.

FieldValue
Service[payments.ledger.v1.LedgerService]
Proto repository / path[git URL] proto/payments/ledger/v1/
Spec version[0.1]
Status[Draft / In review / Approved / Deprecated]
Owning team[@team-ledger]
Syntax[proto3 / edition 2023 / edition 2024]
Endpoint(s)[ledger.internal.example.com:443]
Last updatedYYYY-MM-DD

1. Service purpose

[Example: LedgerService records money movements as immutable, double-entry journal entries and answers balance queries for accounts. It is called by Checkout, Refunds and Payouts. It does not talk to payment providers and does not calculate fees.]

ConsumerMethods usedTraffic (peak)
Example: checkout-servicePostEntry, GetBalance1,200 rps
Example: payouts-workerStreamEntries3 long-lived streams
[Consumer][Methods][Traffic]

2. Protobuf definitions

The excerpt below is the contract for version 1 of the service.

syntax = "proto3";

package payments.ledger.v1;

option go_package = "example.com/gen/payments/ledger/v1;ledgerv1";
option java_multiple_files = true;
option java_package = "com.example.payments.ledger.v1";

import "google/protobuf/timestamp.proto";
import "google/type/money.proto";

// Records journal entries and answers balance queries.
service LedgerService {
  // Posts a balanced journal entry. Idempotent on request_id.
  rpc PostEntry(PostEntryRequest) returns (PostEntryResponse);
  // Returns the current balance of one account.
  rpc GetBalance(GetBalanceRequest) returns (GetBalanceResponse);
  // Streams entries after a cursor, then follows new entries live.
  rpc StreamEntries(StreamEntriesRequest) returns (stream JournalEntry);
}

message PostEntryRequest {
  // Client-generated UUID; repeated calls with the same ID return the original result.
  string request_id = 1;
  repeated Posting postings = 2;
  string description = 3;
  map<string, string> metadata = 4;
}

message Posting {
  string account_id = 1;
  google.type.Money amount = 2;
  Direction direction = 3;
}

enum Direction {
  DIRECTION_UNSPECIFIED = 0;
  DIRECTION_DEBIT = 1;
  DIRECTION_CREDIT = 2;
}

message PostEntryResponse {
  JournalEntry entry = 1;
}

message JournalEntry {
  string entry_id = 1;
  repeated Posting postings = 2;
  google.protobuf.Timestamp posted_at = 3;
  // Field 4 was "legacy_ref" and must not be reused.
  reserved 4;
  reserved "legacy_ref";
}

message GetBalanceRequest {
  string account_id = 1;
}

message GetBalanceResponse {
  google.type.Money balance = 1;
  google.protobuf.Timestamp as_of = 2;
}

message StreamEntriesRequest {
  string account_id = 1;
  // Opaque cursor from a previous entry; empty starts from the beginning.
  string after_cursor = 2;
}

3. Package and versioning

RuleDecision
Package naming[company].[domain].[service].v[major], e.g. payments.ledger.v1
Directory layoutDirectory path mirrors the package
Major versionsNew package v2; v1 and v2 served in parallel for at least [6 months]
Pre-releasev1alpha1, v1beta1 packages carry no compatibility promise
Generated codePublished as [internal package registry] artifacts per language; version follows the proto repository tag
Editions migration[Stay on proto3 / Move to edition 2024 with prototiller after tooling support is confirmed for all client languages]

4. Message and field conventions

  • Messages and enums: PascalCase. Fields: lower_snake_case. Enum values: UPPER_SNAKE_CASE prefixed with the enum name.
  • Every enum has a zero value <ENUM>_UNSPECIFIED = 0 that means "not set".
  • Every RPC has its own request and response messages (<Method>Request, <Method>Response), even if they are small today.
  • Use well-known types: google.protobuf.Timestamp, Duration, FieldMask; use google.type.Money or a decimal string for money, never float or double.
  • Identifiers are strings, not integers, to allow format changes.
  • Use optional (proto3) or field presence (editions) only where "not set" and "default value" must be distinguished.
  • Partial updates take a google.protobuf.FieldMask update_mask.
  • List methods use page_size, page_token and return next_page_token.
  • Every service, RPC, message and field has a leading comment; it becomes the API reference.

5. Field numbering and compatibility

ChangeCompatible?Rule
Add a new field with a new numberYesOld readers ignore unknown fields
Remove a fieldYes, if its number and name are reservedNever reuse the number
Rename a fieldWire-compatible, breaks generated code and JSONTreat as breaking
Change a field's typeNo (except a few documented compatible pairs)New field instead
Change a field numberNoNever
Add an enum valueYes, if clients handle unknown valuesDocument in changelog
Move a field into or out of a oneofNoNew field instead
Add or remove an RPCAdding yes; removing is breakingDeprecate first with option deprecated = true

Field numbers 1 to 15 take one byte to encode; reserve them for frequently set fields. Numbers 19000 to 19999 are reserved by Protocol Buffers. Breaking change detection: [buf breaking / other tool] runs on every pull request against the main branch, mode [WIRE_JSON].

6. Errors

CodeNumberWhen this service returns itClient action
INVALID_ARGUMENT3Unbalanced entry, unknown direction, bad currencyFix request; details in BadRequest
DEADLINE_EXCEEDED4Deadline expired before completionRetry with same request_id if idempotent
NOT_FOUND5Account does not existDo not retry
ALREADY_EXISTS6Not used: duplicates return the original resultn/a
PERMISSION_DENIED7Caller not allowed to post to this accountDo not retry
RESOURCE_EXHAUSTED8Per-client quota exceededBack off per RetryInfo
FAILED_PRECONDITION9Account frozen or closedDo not retry until state changes
ABORTED10Concurrency conflict on balance updateRetry the whole operation
UNIMPLEMENTED12Method not available in this versionDo not retry
INTERNAL13Invariant broken; bugAlert; do not retry blindly
UNAVAILABLE14Transient: instance draining or overloadedRetry with backoff
UNAUTHENTICATED16Missing or invalid credentialsRefresh token, then retry

Error details carry google.rpc.ErrorInfo with reason (for example ACCOUNT_FROZEN) and domain (ledger.example.com). Messages are for developers and MUST NOT contain account balances or personal data.

7. Deadlines, retries and limits

RPCTypeRecommended deadlineIdempotentRetry policyMax message size
PostEntryUnary2 sYes (request_id)3 attempts on UNAVAILABLE, backoff 100 ms to 1 s1 MB
GetBalanceUnary300 msYes3 attempts on UNAVAILABLE, DEADLINE_EXCEEDED64 KB
StreamEntriesServer streamingNone (long-lived), keepalive 60 sResume via cursorReconnect with last cursor1 MB per message
[RPC][Type][Deadline][Yes / No][Policy][Size]

Deadlines propagate: a server calling another service passes on the remaining time minus a margin. Retry budget: at most [10%] extra load from retries; hedging is [not used].

8. Streaming types

TypeUsed byNotes
UnaryPostEntry, GetBalanceDefault choice
Server streamingStreamEntriesOrdered by posting time; each message carries a cursor for resumption
Client streaming[Not used][Reason]
Bidirectional streaming[Not used][Reason]

The sequence shows how a stream consumer resumes after a disconnect.

9. Authentication and authorisation

LayerMechanism
TransportTLS 1.3; mutual TLS between services through [service mesh / SPIFFE identities]
Caller identityWorkload identity from mTLS certificate; end-user context as a JWT in authorization metadata
AuthorisationPer-method policy: PostEntry only for [checkout, refunds]; GetBalance for [listed callers]
Metadatax-request-id propagated; no secrets in custom metadata keys

10. Observability

  • Metrics per method: request count by status code, latency histogram, in-flight streams, message sizes.
  • Traces: OpenTelemetry gRPC instrumentation; trace context propagated in metadata.
  • Logs: method, status code, duration, caller identity, request_id; never message payloads with money amounts at INFO level.
  • Health: implements grpc.health.v1.Health; readiness fails while draining.
  • Reflection: enabled in [non-production only].

11. Load balancing and deployment

TopicDecision
Balancing[Client-side round_robin via DNS / xDS] or [L7 proxy (service mesh)]
Connection managementMAX_CONNECTION_AGE [5 min] on servers so clients rebalance
KeepaliveClient pings every [60 s] on idle streams; server permits it
DrainingServer sends GOAWAY and fails health checks before shutdown
Browser clients[gRPC-Web through proxy / not supported]

12. Changelog

DateProto tagChangeCompatible
YYYY-MM-DDExample: v1.3.0Added metadata to PostEntryRequestYes
YYYY-MM-DD[Tag][Change][Yes / No]

Standard and licence note

Based on the public gRPC documentation (https://grpc.io/docs/) and the Protocol Buffers language guides and Editions overview (https://protobuf.dev/), including the canonical status codes and the google.rpc error model. Examples use proto3 syntax; Protobuf Editions 2023 and 2024 are the released editions at the time of writing. All text and examples are original to this collection. Template licence: MIT.

Схожі шаблони