Expert knowledge for Protocol Buffer development. Includes style guidelines, BUILD rules, and proto_lint_test requirements.
Use this skill when the user creates, modifies, or reviews .proto files or proto BUILD rules.
Every proto_library rule MUST have a corresponding proto_lint_test rule. No exceptions for new code.
load("@rules_proto//proto:defs.bzl", "proto_library")
load("//build_rules/proto:build_defs.bzl", "proto_lint_test")
proto_library(
name = "example_proto",
srcs = ["example.proto"],
)
proto_lint_test(
name = "example_proto_lint",
protos = [":example_proto"],
)
{name}_proto{name}_proto_lint| Element | Style | Example |
|---|---|---|
| Message names | PascalCase | UserProfile |
| Field names | snake_case | user_name |
| Enum names | PascalCase | StatusCode |
| Enum values | SCREAMING_SNAKE_CASE | STATUS_ACTIVE |
| Enum zero value | Must end with _UNKNOWN | STATUS_UNKNOWN = 0 |
All of these MUST have documentation comments:
Write protobuf comments like detailed fine-print contracts. When other developers read your code just by looking at the field name and type, they should know exactly what values to expect. If not, use a better name or add more comments/examples.
Always show example values in field comments when the format isn't obvious:
// Bad - unclear format
string timestamp = 1;
string software_version = 2;
// Good - with examples
// Represents different robot process errors as a 3-character string.
// E.g. "001", "022"
string error_code = 1;
// The unique robot identifier. E.g. "pennybot-123abc"
string robot_id = 2;
// Software version in release format. E.g. "22.01", "release-22.01"
string software_version = 3;
In proto3, unset scalar fields are indistinguishable from zero values. Document what empty/zero means:
// The user's preferred language code (ISO 639-1).
// E.g. "en", "ko". Empty string means system default will be used.
string language_code = 1;
// Maximum retry attempts. Zero means no retries (fail immediately).
int32 max_retries = 2;
// Optional deadline for the operation.
// If not set (null), the operation has no timeout.
google.protobuf.Timestamp deadline = 3;
For RPC services, document possible error codes and failure conditions:
service RobotService {
// GetStatus returns the current robot status.
//
// Errors:
// - NOT_FOUND: Robot with the given ID does not exist.
// - UNAVAILABLE: Robot is offline or unreachable.
// - PERMISSION_DENIED: Caller lacks permission to access this robot.
rpc GetStatus(GetStatusRequest) returns (GetStatusResponse);
// ExecuteCommand sends a command to the robot.
//
// Errors:
// - INVALID_ARGUMENT: Command parameters are malformed.
// - FAILED_PRECONDITION: Robot is not in a state to execute command
// (e.g., emergency stop is active).
// - DEADLINE_EXCEEDED: Robot did not respond within timeout.
rpc ExecuteCommand(ExecuteCommandRequest) returns (ExecuteCommandResponse);
}
// UserProfile represents a user's public profile information.
message UserProfile {
// The unique identifier for the user. E.g. "user-abc123"
// Empty string is invalid and should never occur.
string user_id = 1;
// User's display name. E.g. "John Doe"
// Empty string means the user has not set a display name.
string display_name = 2;
// Status indicates the user's current account status.
enum Status {
// STATUS_UNKNOWN is the default unset value.
// Treat as an error if received.
STATUS_UNKNOWN = 0;
// STATUS_ACTIVE indicates the user is active and can use the system.
STATUS_ACTIVE = 1;
// STATUS_SUSPENDED indicates the user is temporarily suspended.
STATUS_SUSPENDED = 2;
}
// Current account status. STATUS_UNKNOWN should never be set explicitly.
Status status = 3;
}
syntax = "proto3";
package bearrobotics.path.to.package;
option go_package = "github.com/bearrobotics/pennybot/path/to/package/name_proto";
// Imports here
// Messages and enums here (alphabetically ordered)
When generating code for multiple languages, follow this pattern:
load("@com_google_protobuf//:protobuf.bzl", "py_proto_library")
load("@io_bazel_rules_go//proto:def.bzl", "go_proto_library")
load("@rules_cc//cc:defs.bzl", "cc_proto_library")
load("@rules_proto//proto:defs.bzl", "proto_library")
load("//build_rules/proto:build_defs.bzl", "proto_lint_test")
proto_library(
name = "example_proto",
srcs = ["example.proto"],
)
cc_proto_library(
name = "example_cc_proto",
deps = [":example_proto"],
)
go_proto_library(
name = "example_go_proto",
importpath = "github.com/bearrobotics/pennybot/path/to/example_proto",
protos = [":example_proto"],
)
py_proto_library(
name = "example_py_proto",
srcs = ["example.proto"],
)
proto_lint_test(
name = "example_proto_lint",
protos = [":example_proto"],
)
Use the universal formatter:
bazel run //:format
DO NOT GUESS on advanced topics. Read the authoritative documentation:
docs/gdr/docs/eng_docs/guides/style_guides/protobuf.mdRead this file when:
build_rules/proto/README.mdRead this file when:
proto_lint_test optionsbearrobotics/api/README.mdRead this file when:
Run the coverage checker to ensure all protos have lint tests:
./bin/tests/check_proto_lint_coverage.py