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
messagemust be annotated as a resource (AIP-123). - The
messagename 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
snapshotmust be the configuration of the parent at the point in time the revision was created.
- The value of
- The resource revision must contain a
created_atfield (see AIP-142). - The resource revision must contain an
objectfield with value{resource_name}_revision. - The resource revision must contain an integer
versionfield.- The
revision_numberfield must start at 1 for the first revision of a resource. - Each new revision must increment the
revision_numberby exactly 1.
- The
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
objectandidfields.