This AIP is currently a draft. This means that it is being actively debated and discussed, and it may change in non-trivial ways.

AIP-162

Resource Revisions

Some APIs need to have resources with a revision history, where users can reason about the state of the resource over time. There are several reasons for this:

  • Users may want to be able to roll back to a previous revision, or diff against a previous revision.
  • An API may create data which is derived in some way from a resource at a given point in time. In these cases, it may be desirable to snapshot the resource for reference later.

Note: We use the word revision to refer to a historical reference for a particular resource, and intentionally avoid the term version, which refers to the version of an API as a whole.

Guidance

APIs may store a revision history for a resource. Examples of when it is useful include:

  • When it is valuable to expose older versions of a resource via an API. This can avoid the overhead of the customers having to write their own API to store and enable retrieval of revisions.
  • Other resources depend on different revisions of a resource.
  • There is a need to represent the change of a resource over time.

APIs implementing resources with a revision history must abstract resource revisions as a top level collection.

message InvoiceRevision {
  // The resource type. Always set to "invoice_revision".
  string object = 1 [
    (google.api.field_behavior) = OUTPUT_ONLY,
    (google.api.field_behavior) = REQUIRED,
    (buf.validate.field).string.const = "invoice_revision"
  ];

  string id = 2 [(google.api.field_behavior) = IDENTIFIER];

  // The snapshot of the invoice.
  Invoice snapshot = 3 [
    (google.api.field_behavior) = OUTPUT_ONLY,
    (google.api.field_behavior) = REQUIRED
  ];

  // The revision number of the revision, auto-incremented for each new revision of an invoice.
  int revision_number = 4 [
    (google.api.field_behavior) = OUTPUT_ONLY,
    (google.api.field_behavior) = REQUIRED
  ];

  // The timestamp that the revision was created.
  google.protobuf.Timestamp created_at = 5 [
    (google.api.field_behavior) = OUTPUT_ONLY,
    (google.api.field_behavior) = REQUIRED
  ];
}
  • The message must be annotated as a resource (AIP-123).
  • The message name must be named {ResourceType}Revision.
  • The resource revision must contain a field with a message type of the parent resource, with a field name of snapshot.
    • The value of snapshot must be the configuration of the parent at the point in time the revision was created.
  • The resource revision must contain a created_at field (see AIP-142).
  • The resource revision must contain an object field with value {resource_name}_revision.
  • The resource revision must contain an integer version field.
    • The revision_number field must start at 1 for the first revision of a resource.
    • Each new revision must increment the revision_number by exactly 1.

Creating Revisions

Depending on the resource, different APIs may have different strategies for

  • Create a new revision any time that there is a change to the parent resource
  • Create a new revision when important system state changes
  • Create a new revision when specifically requested

APIs may use any of these strategies. APIs must document their revision creation strategy.

Resource ids for revisions

Resource revisions have ids with the format {resource_id}@{version}. For example:

c6244ea6-eae5-43cf-8a8c-c47516d95cd7@5

Child resources

Resources with a revision history may have child resources. If they do, there are two potential variants:

  • Child resources where each child resource is a child of the parent resource as a whole.
  • Child resources where each child resource is a child of a single revision of the parent resource.

APIs should not include multiple levels of resources with revisions, as this quickly becomes difficult to reason about.

Standard methods

Any standard methods must implement the corresponding AIPs (AIP-131, AIP-132, AIP-133, AIP-134, AIP-135), with the following additional behaviors:

  • List methods: Revisions in the list response must be ordered in reverse chronological order.

Rationale

Resource revision ID format

The {resource_id}@{version} format for revision IDs aligns with versioning patterns within Evy prior to 2025-05, enabling a smoother migration path (ie. Policy). This approach is inspired by the pre 2023-09 iteration of Google's AIP-162 which used the @ symbol as a collection extension.

Using a sequential version number provides intuitive ordering and makes the revision sequence immediately apparent. Combining this with the resource ID ensures each revision ID is globally unique while remaining easily derivable from its parent resource, simplifying reference and retrieval operations.

Abstract revisions as top level collection

Revisions being resources makes revisions a first class citizen.

  • Revisions can offer standard get, list, and delete methods.
  • It retains the flexibility of extending new fields to revision in addition to the resource message.

Output only resource configuration

Although it was an option to have the revision take in the resource configuration as part of the create method, doing so would have allowed users to submit resource configuration for a revision that the resource was never in.

OUTPUT_ONLY and requiring that a created revision represents the resource at current point in time eliminates that issue.

History

Divergence from Google AIP

This AIP diverges from Google AIP-162 in several key aspects. Resource revisions are implemented as a top-level collection rather than a subcollection of the parent resource, providing more flexibility and clearer separation of concerns.

The revision ID format uses {resource_id}@{version} with an incremented integer version, contrasting with Google's approach which uses partial uuid revision IDs.

The message structure includes mandatory object and id fields consistent with other Evy resources, and explicitly requires a version field for tracking the revision sequence.

Switching from a collection extension to a subcollection on Google AIP

On 2023-09 of Google AIP-162, revisions are abstracted as a nested resource collection. Prior to this, revisions are more like extension of an existing resource by using @ symbol. List and delete revisions were custom methods on the resource collection. A single Get method was used to retrieve either the resource revision, or the resource.

Its primary advantage was allowing a resource reference to seamlessly refer to a resource, or its revision.

It also had several disadvantages:

  • List revisions is a custom method (:listRevisions) on the resource collection
  • Delete revision is a custom method on the resource collection
  • Not visible in API discovery doc
  • Resource ID cannot use @

The guidance was modified ultimately to enable revisions to behave like a resource, which reduces the users cognitive load and allows resource-oriented clients to easily list, get, create, and update revisions.

Changelog

  • 2025-06-12: Initial version forked from Google's AIP-162 with changes to revision implementation, including top-level collection approach, version-based IDs, and standardized message structure with object and id fields.