Implementing gRPC Best Practices
1. Following Naming Conventions
| Element | Convention |
|---|---|
| Package | lower.snake.v1 |
| Service | PascalCase + "Service" |
| Method | VerbNoun (GetUser, ListUsers) |
| Message | PascalCase; RequestNameRequest / ResponseNameResponse |
| Field | lower_snake_case |
| Enum value | UPPER_SNAKE; first = UNSPECIFIED = 0 |
2. Versioning APIs Properly
| Rule | Detail |
|---|---|
| Version in package | user.v1 not URL |
| Breaking change | Bump major (v2) |
| Stable interface | Avoid breaking even minor |
3. Designing Idempotent Methods
| Pattern | Detail |
|---|---|
| Get / List | Naturally idempotent |
| Create | Use idempotency key |
| Update | Use field mask + ETag |
| Delete | Idempotent — second call returns NOT_FOUND or OK |
4. Using Appropriate RPC Type
| Choice | When |
|---|---|
| Unary | Simple request/response |
| Server stream | Large or open-ended result set |
| Client stream | Uploads, batched writes |
| Bidi stream | Real-time / interactive |
5. Setting Proper Deadlines
| Rule | Detail |
|---|---|
| Always set | Client side, every call |
| Propagate | Pass ctx into downstream calls |
| Budget allocation | Sub-call deadline < remaining |
6. Handling Errors Properly
| Rule | Detail |
|---|---|
| Use canonical codes | Don't overload INTERNAL |
| Rich details | ErrorInfo, BadRequest, RetryInfo |
| Never leak internals | Map errors at boundary |
7. Documenting Services
| Where | Detail |
|---|---|
| Proto comments | Above service / method / message / field |
| Generated docs | protoc-gen-doc, buf.build registry |
| Examples | Include sample requests & responses |
8. Monitoring Services
| Signal | Detail |
|---|---|
| RED metrics | Rate, Errors, Duration |
| SLI/SLO | Availability + latency target per method |
| Alerting | Multi-window multi-burn-rate |
9. Testing Thoroughly
| Layer | Detail |
|---|---|
| Unit | Service methods in isolation |
| Integration | bufconn + real deps |
| Contract | Proto compat across versions |
| Load | ghz / k6 against staging |
10. Securing Communications
| Default | Detail |
|---|---|
| TLS everywhere | Even internal mesh |
| mTLS for service-to-service | Mutual identity |
| Rotate keys/certs | Automate via cert-manager / SPIRE |
11. Implementing Graceful Shutdown
Example: Signal-driven shutdown
sig := make(chan os.Signal, 1)
signal.Notify(sig, syscall.SIGINT, syscall.SIGTERM)
<-sig
h.Shutdown() // mark health NOT_SERVING
time.Sleep(2 * time.Second) // let LB notice
done := make(chan struct{})
go func() { srv.GracefulStop(); close(done) }()
select {
case <-done:
case <-time.After(30 * time.Second):
srv.Stop()
}
12. Reviewing Code Regularly
| Practice | Detail |
|---|---|
| Proto review | Required for any .proto change |
| Backward compat check | buf breaking in CI |
| Lint | buf lint enforces style |
| Security review | For auth, crypto, input validation changes |
| Performance review | For new streaming or hot-path RPCs |