Understanding Protocol Buffers Syntax
1. Understanding Proto3 Syntax
| Element | Syntax |
| Version | syntax = "proto3"; (must be first non-comment line) |
| Package | package company.product.v1; |
| Import | import "other.proto"; |
| Option | option go_package = "..."; |
| Message | message Name { field_type name = N; } |
| Enum | enum Color { UNSPECIFIED = 0; RED = 1; } |
| Service | service Foo { rpc Bar(Req) returns (Res); } |
2. Understanding Message Structure
Example: Anatomy of a message
message Order {
string id = 1; // scalar
repeated LineItem items = 2; // list
Customer customer = 3; // nested message
Status status = 4; // enum
map<string, string> metadata = 5; // map
oneof payment { // mutually exclusive
Card card = 6;
Wallet wallet = 7;
}
}
| Part | Description |
| Field name | snake_case identifier |
| Field number | Unique positive int — used in wire format |
| Type | Scalar, message, enum, map, oneof, repeated |
3. Understanding Field Numbering
| Range | Bytes on Wire | Use |
| 1–15 | 1 byte tag | Frequently used fields |
| 16–2047 | 2 byte tag | Less-used fields |
| 2048–536,870,911 | 3–5 byte tag | Avoid unless needed |
| 19000–19999 | Reserved | Protobuf internal — never use |
Warning: Never reuse or renumber a field. Mark removed numbers/names with reserved to prevent silent data corruption.
4. Understanding Field Types
| Category | Types |
| Integer | int32, int64, uint32, uint64, sint32, sint64, fixed32, fixed64, sfixed32, sfixed64 |
| Float | float, double |
| Other scalar | bool, string, bytes |
| Composite | message, enum, map<K,V>, oneof, repeated |
5. Understanding Field Rules
| Rule | Meaning (proto3) |
| singular (default) | 0 or 1 occurrence; presence not tracked for scalars |
optional | Presence tracking — distinguishes unset from default |
repeated | Zero or more; ordered list |
map<K,V> | Unordered key/value pairs |
6. Understanding Default Values
| Type | Default |
| Numeric | 0 |
| bool | false |
| string | "" |
| bytes | empty |
| enum | First value (must be 0) |
| message | null/unset |
| repeated/map | Empty |
Note: Default-valued scalar fields are not transmitted on the wire. Use optional to preserve explicit-zero semantics.
7. Understanding Package Namespaces
| Option | Language Mapping |
package x.y.v1; | Universal namespace; prevents naming conflicts |
option go_package | Go import path: "example.com/api/x/v1;xv1" |
option java_package | Java: "com.example.api.x.v1" |
option csharp_namespace | .NET: "Example.Api.X.V1" |
8. Understanding Import Mechanism
| Form | Purpose |
import "a.proto"; | Standard import |
import public "a.proto"; | Re-export to importers (transitive) |
import weak "a.proto"; | Optional — discouraged |
-I/--proto_path | Search roots for protoc |
9. Understanding Proto Compilation Process
Compilation Pipeline
protoc parses .proto files into descriptors
- Resolves imports across
--proto_path
- Validates syntax, types, and field numbers
- Builds a
FileDescriptorSet
- Invokes each
protoc-gen-* plugin via stdin/stdout
- Plugin writes generated source files to
--*_out
| Syntax | Purpose |
// line | Single-line comment |
/* block */ | Multi-line comment |
| Leading comment above element | Captured by tools (Buf, docs generators) as element documentation |