Nice post! Well-explained, especially the FAQ. Nevertheless, I want to point out one extra complication (I believe you are aware of it, but some readers might not be).
The FAQ section states guide lines like:
You can safely add a non-optional field if the record appears only in method return types.
Now, this is true if you are only considering methods in types of service declarations, like:
service : {
f : () -> my_record_type
}
However, and here is the subtle part, it is not true for service types in general. In Candid, you can freely define service types themselves. And because Candid is higher-order, it again depends on how such a type is used.
Here is an example. Consider:
type R = record { a : nat }
type S = service { callback : () -> R }
service : {
register : (s : S) -> ()
}
This is a service that takes another service as argument. And that other service has R in the result of a method (only). Yet, it would be a breaking change (and is disallowed) to extend R with a non-optional field, because S itself occurs as a parameter, so the direction of all the rules regarding extensions of S has to switch around. It is, in fact, safe (and allowed) to remove field a
in this particular example.
I expect that most devs will never run into this, because they don’t use service types (or function types) as parameters. But if you ever do, you might want to be aware.
So much for my nitpicking.