Protobuf’s "well-known types" aren’t just syntactic sugar; they’re a deliberate escape hatch from pure, static schema definition into a world of dynamic data structures and temporal reasoning.

Let’s see google.protobuf.Timestamp in action. Imagine you’re logging events. Instead of a string like "2023-10-27T10:30:00Z" which requires parsing and validation on every read, you use Timestamp.

syntax = "proto3";

import "google/protobuf/timestamp.proto";

message LogEntry {
  string message = 1;
  google.protobuf.Timestamp timestamp = 2;
}

When you serialize this, the timestamp field becomes a struct with two fields: seconds (an int64 representing Unix epoch seconds) and nanos (an int32 for nanosecond precision within that second).

{
  "message": "Server started",
  "timestamp": {
    "seconds": 1698393000,
    "nanos": 123456789
  }
}

This structure is universally understood by protobuf libraries. Your Go application can create this with timestamppb.Now(), your Python app with timestamp_pb2.Timestamp(seconds=..., nanos=...), and any other language seamlessly. The serialization format is standardized, and the libraries handle the conversion to/from native time types.

The google.protobuf.Duration type works similarly, representing a span of time. It also has seconds and nanos fields, but these represent an interval, not a point in time. This is crucial for operations like calculating how long a process took.

syntax = "proto3";

import "google/protobuf/duration.proto";

message ProcessResult {
  string process_id = 1;
  google.protobuf.Duration duration = 2;
  bool success = 3;
}

A Duration of "5 minutes and 30 seconds" would be serialized as:

{
  "process_id": "abc-123",
  "duration": {
    "seconds": 330,
    "nanos": 0
  },
  "success": true
}

The real power emerges with google.protobuf.Struct. This is protobuf’s answer to dynamic, schema-less data within a strongly-typed protobuf message. Think of it as a JSON object embedded directly into your protobuf. A Struct contains a map where keys are strings, and values can be any other primitive type, another Struct, a list of values (ListValue), or even null (NullValue).

syntax = "proto3";

import "google/protobuf/struct.proto";

message UserProfile {
  string user_id = 1;
  google.protobuf.Struct metadata = 2;
}

Here’s a UserProfile with arbitrary metadata:

{
  "user_id": "user-456",
  "metadata": {
    "fields": {
      "last_login": {
        "seconds": 1698392400,
        "nanos": 0
      },
      "preferences": {
        "fields": {
          "theme": {
            "string_value": "dark"
          },
          "notifications_enabled": {
            "bool_value": true
          }
        }
      },
      "login_attempts": {
        "number_value": 5
      }
    }
  }
}

Notice how last_login is a Timestamp (represented by fields containing seconds and nanos), theme is a string (string_value), notifications_enabled is a boolean (bool_value), and login_attempts is a number (number_value). The Struct type allows you to embed nested structures and lists, mirroring JSON’s flexibility. This is incredibly useful for situations where the exact shape of configuration, user-defined fields, or event payloads isn’t known at compile time. You can serialize a JSON object directly into a Struct and vice-versa in many languages, bridging the gap between JSON-native systems and protobuf.

When you define a Struct, you’re not defining a fixed schema for its contents in the way you do with other protobuf fields. Instead, you’re defining a container that can hold a dynamic set of key-value pairs, where the values themselves have a well-defined, albeit flexible, set of possible types. This means that at runtime, your code needs to be prepared to handle the absence of expected keys or the presence of unexpected ones, performing type checks on the values retrieved from the Struct.

The next challenge is handling versioning of messages that contain these dynamic types, especially when your Struct fields evolve independently of the main message schema.

Want structured learning?

Take the full Protobuf course →