AIP-200

Bad API precedent

Many times, APIs are written in ways that do not match new guidance that is added to these standards after those APIs have already been released. Additionally, sometimes it can make sense to intentionally violate standards for particular reasons, such as maintaining consistency with established systems, meeting stringent performance requirements, or other practical concerns. Finally, as carefully as everyone reviews APIs before they are released, sometimes mistakes can slip through.

Since it often is not feasible to fix past mistakes or make the standards serve every use case, we may be stuck with these exceptions for quite some time. Further, since new APIs often base their designs (names, types, structures, etc) on existing APIs, it is possible that a standards violation in one API could spill over into other APIs, even if original reason for the exception is not applicable to the other APIs.

As a result of this problem, it is important to “stop the bleeding” of these standards exceptions into new APIs, and additionally document the reasons for each exception so that historical wisdom is not lost.

Guidance

If an API violates the AIP standards for any reason, there must be an internal comment linking to this document using its descriptive link (aip.dev/not-precedent) to ensure others do not copy the violations or cite the errors as precedent of a “previously approved API”.

The comment should also include an explanation of what violates standards and why it is necessary. For example:

message DailyMaintenanceWindow {
  // Time within the maintenance window to start the maintenance operations.
  // It must use the format "HH MM", where HH : [00-23] and MM : [00-59] GMT.
  // (-- aip.dev/not-precedent: This was designed for consistency with crontab,
  //     and preceded the AIP standards.
  //     Ordinarily, this type should be `google.type.TimeOfDay`. --)
  string start_time = 2;

  // Output only. Duration of the time window, automatically chosen to be
  // smallest possible in the given scenario.
  // (-- aip.dev/not-precedent: This preceded the AIP standards.
  //     Ordinarily, this type should be `google.protobuf.Duration`. --)
  string duration = 3;
}

Additional Guidance

APIs should only be considered to be precedent-setting if they are in beta or GA.

Possible reasons for violating standards

Local consistency

If your API violates a standard throughout, it would be jarring and frustrating to users to break the existing pattern only for the sake of adhering to the global standard.

For example, if all of your resources use creation_time (instead of the standard field create_time), a new resource in your API should continue to follow the pattern.

However, others who might copy your API should be made aware that this is contra-standard and not something to cite as precedent when launching new APIs.

// ...
message Book {
  // (-- aip.dev/not-precedent: This field was present before there was a
  //     standard field.
  //     Ordinarily, it should be spelled `create_time`. --)
  google.protobuf.Timestamp creation_time = 1;
}

// ...
message Author {
  // (-- aip.dev/not-precedent: This field was present before there was a
  //     standard field.
  //     Ordinarily, it should be spelled `create_time`. --)
  google.protobuf.Timestamp creation_time = 1;
}

Already GA (Grandfathered)

Standards violations are sometimes overlooked before launching, resulting in APIs that enter the GA stage and therefore can not easily be modified. Additionally, a stable API may pre-date a standards requirement. In these scenarios, we recognize the difficulty in fixing the problem; however, we want to avoid others’ copying the mistake and citing the existing API as a reason why their design should be approved.

Adherence to external spec

Occasionally, APIs must violate standards because specific requests are implementations of an external specification (for example, OAuth), and their specification may be at odds with our style guide. In this case, it is likely to be appropriate to follow the external specification.

Adherence to existing systems

Similar to the example of an external specification above, it may be proper for an API to violate standards to fit in with an existing system in some way. This is a fundamentally similar case where it is wise to meet the customer where they are. A potential example of this might be integration with or similarity to a partner API.

Expediency

Sometimes we have users who need an API surface by a very hard deadline or money walks away. Since we are running a business here, there will be times when we recognize the API could be better but cannot get it that way and into users’ hands before the deadline. In those cases, there are exceptions granted to ship APIs that violate standards due to time and business constraints.

Technical concerns

Sometimes our internal systems have very specific implementation needs (e.g., they rely on operation transforms that speak UTF-16, not UTF-8) and adhering to standards would require extra work that does not add much value to API consumers. Future systems which are likely to expose an API at some point should bear this in mind to avoid building underlying infrastructure which makes it hard to follow standards.

Changelog

  • 2019-05-04: Changed to a public link (aip.dev/not-precedent), and changed references to “the style guide” to use the more generic term “standards” (to account for a general shift to AIPs).