AIP-2604
Numeric arguments
Some API fields refer to either a percentage or a fixed number. For example, in GCE a group can be created with a configurable limit for either the percentage or number of instances in the group that can undergo maintenance simultaneously:
message ConcurrencyControl {
enum LimitType {
INVALID = 0;
PERCENT = 1;
FIXED = 2;
}
optional int32 concurrency_limit = 1;
optional LimitType limit_type = 2;
}
Guidance
Flag layout
In gcloud, such API fields should correspond to a mutually exclusive group consisting of two flags: one for specifying a number, and the other for specifying a percentage. The API field in the example above would thus correspond to the following surface specification in gcloud:
- group:
mutex: true
arguments:
- name: concurrency-limit
help_text: |
Maximum number of instances in the group that can undergo maintenance
simultaneously.
- name: concurrency-limit-percent
help_text: |
Integer from 0 to 100 representing the maximum percentage of instances
in the group that can undergo maintenance simultaneously.
Flag naming
Any flag taking a percentage should end with -percent
(not -percentage
).
Flag type
Any flag taking a percentage should take an integer from 0 to 100. If more precision is required, it is acceptable to take a float from 0 to 100.
Alternatives considered
One flag, where percentage is specified if the flag value ends with '%', and number is assumed otherwise
In other words, to specify 10% one would use --concurrency-limit=10%
, and to
specify 10 instances one would use --concurrency-limit=10
.
This would be a cleaner design since it naturally maps a single concept (the
limit) to a single flag (--concurrency-limit
), avoiding the need for
additional flags that clutter the help text.
However, the main reason not to prefer this approach is that it significantly increases the risk of user error. For instance, suppose a group currently contains 20 instances with a concurrency limit of 10%, and a user wishes to update the limit to 20%. If the user issues a describe command (or performs a GET request) to see the existing limit, the API response will contain something like:
concurrencyControl:
concurrencyLimit: 10
limitType: PERCENT
It's easy for the user to gloss over limitType and just assume that
--concurrency-limit=20
will update the limit to 20%. However, in reality this
would set the limit to 20 instances (100% of the group), potentially leading to
catastrophic consequences.
The recommend approach avoids this ambiguity at the expense of some elegance, but we deem this tradeoff necessary.
One flag, where percentage is specified if the flag value ends with '%', and number is specified if the flag value ends with 'n'
In other words, to specify 10% one would use --concurrency-limit=10%
, and to
specify 10 instances one would use --concurrency-limit=10n
. It would be an
error to provide a value that doesn't end in '%' or 'n'.
While this avoids the potential for a user to confuse percentages and numbers, requiring 'n' at the end of a number is inelegant UX. A number should obviously resemble a number, and the choice of 'n' is also rather arbitrary.
Exceptions
This CIP does not apply for API fields like timestamps, where we allow the user to enter either absolute times or durations in the same flag. This is because absolute times and durations have natural formats that differ, and thus there's no potential for a user to confuse the two. In general if this is the case, one flag that accepts both formats should be preferred for the sake of UX simplicity.