Date: 2019-03
Author: thatguy@google.com
Contributors: pascallouis@google.com, yifeit@google.com, cramertj@google.com
This doc is written for engineers who want to evolve FIDL APIs. It describes what can be done safely without disrupting fellow teammates or downstream clients.
ABI (application binary interface) compatibility is concerned with the encoding and decoding of data over binary interfaces. FIDL messages (method calls on FIDL protocols) end up serialized as bytes over a zircon channel. Both channel endpoints (the client and server) must agree on the size, ordering and meaning of the bytes. A mismatch in expectations leads to binary incompatibility.
A change to a FIDL type that is source compatible (also known as API compatible) means it is possible for someone to write code using the generated code for a type that compiles both before and after the change is made. Making a source-incompatible change requires changing all client source code at the same time (difficult if clients exist outside the repository) to avoid breaking builds.
Note: Since Fuchsia sources (and related product code) exist in multiple repositories with integration rollers and SDK releases between them, it is not enough to ensure that the fuchsia.git repository compiles.
Disclaimer: The FIDL compatibility story today contains a number of edge cases. Language bindings may expose interfaces whose usage may or may not be resilient to changes in the underlying FIDL protocol. There are ongoing efforts to standardize these interfaces, but in the meantime, this document exists as a best-effort guide towards what types of code may be broken by what sorts of FIDL changes. If you discover an omission or mistake in this document, please suggest the appropriate change, and refrain from enacting retribution on this document's authors.
General guidance: once a struct is defined and in use, it cannot be changed.
struct A { struct A_new { int32 a; int32 a; string b; string b; }; };
ABI Compatibility: YES
Transition Considerations:
struct A { struct A { int32 a; string b; string b; int32 a; }; };
ABI Compatibility: NO
Transition Considerations:
auto a = A{10, "foo"};
A {10, "foo"};
auto a = A{
.a = 10,
.b = "foo"
}
;a := A{
A:10.
B: "foo",
}
struct A { struct A { int32 a; int32 a_new; string b; string b; }; };
ABI Compatibility: YES
Transition Considerations: NO
f(a.a_new)
and auto a = A{.a = 10};
struct A { struct A { int32 a; int32 a; string b; string b; int32 c; }; };
ABI Compatibility: NO
Transition Considerations:
struct A { struct A { int32 a; int32 a; string b; }; };
ABI Compatibility: NO
Transition Consideration:
b
is not referenced any more (including in positional initializers).table T { table T_new { 1: int32 a; 1: int32 a; 2: string b; 2: string b; }; };
ABI Compatibility: YES
table T { table T { 1: int32 a; 2: string b; 2: string b; 1: int32 a; }; };
ABI Compatibility: YES
Transition Considerations: YES
table T { table T { 1: int32 a; 1: int32 a_new; 2: string b; 2: string b; }; };
ABI Compatibility: YES
Transition Considerations: NO
table T { table T { 1: int32 a; 1: int32 a; 2: string b; 2: string b; 3: int32 c; }; };
ABI Compatibility: YES
Transition Considerations: YES
table T { table T { 1: int32 a; 1: int32 a; 2: string b; }; };
ABI Compatibility: YES
Transition Considerations: YES
b
is not referenced any more.[NoHandles] table T { table T { 1: int32 a; 1: int32 a; 2: string b; 2: string b; }; };
ABI Compatibility: TODO
Transition Considerations: TODO
union A { union A { 1: int32 a; 2: string b; 2: string b; 1: int32 a; }; };
ABI Compatibility: YES
Transition Considerations: YES
union A { union A { 1: int32 a; 1: int32 a_new; 2: string b; 2: string b; }; };
ABI Compatibility: YES
Transition Considerations: NO
union A { union A { 1: int32 a; 1: int32 a; 2: string b; 2: string b; 3: int32 c; }; };
ABI Compatibility: YES
Transition Considerations:
switch{}
on union tag) will break, so source needs to be updated accordingly.union A { union A { 1: int32 a; 1: int32 a; 2: string b; 2: reserved; }; };
ABI Compatibility: YES
Transition Considerations: YES
vector<T>:N vector<T>:M
ABI Compatibility: YES
Transition Considerations:
M > N
) then all consumers MUST be updated first.M < N
) then all producers MUST be updated first.vector<T>:N vector<U>:N
In many cases, this is neither ABI compatible, nor transitionable. Specific cases can be discussed, but do not rely on this for evolvability of your protocols.
ABI Compatibility: DEPENDS
Transition Considerations: DEPENDS
Similar to vectors.
enum E { enum E { A = 1; B = 2; B = 2; A = 1; }; };
ABI Compatibility: YES
Transition Considerations: YES
enum E { enum E { A = 1; A_NEW = 1; B = 2; B = 2; }; };
ABI Compatibility: YES
Transition Considerations: NO
enum E { enum E { A = 1; A = 1; B = 2; B = 2; C = 3; }; };
ABI Compatibility: YES
Transition Considerations:
switch{}
without default
will breakmatch
without "_"
will breakenum E { enum E { A = 1; A = 1; B = 2; }; };
ABI Compatibility: YES
Transition Considerations:
E::B
will break[Discoverable] [Discoverable] protocol P { protocol P_new { M1() -> (); M1() -> (); M2() -> (); M2() -> (); }; };
ABI Compatibility: NO
Transition Considerations: NO
protocol P { protocol P_new { M1() -> (); M1() -> (); M2() -> (); M2() -> (); }; };
ABI Compatibility: NO
Transition Considerations: NO
library A: library A.new: protocol P { protocol P { M1() -> (); M1() -> (); M2() -> (); M2() -> (); }; };
ABI Compatibility: NO
Transition Considerations: NO
Note: These rules apply only to the methods, their names & ordering. Protocol method arguments and return values follow the same rules as structs.
protocol P { protocol P { M1() -> (); M2() -> (); M2() -> (); M1() -> (); }; };
ABI Compatibility: YES
Transition Considerations: YES
protocol P { protocol P { M1() -> (); M1_new() -> (); M2() -> (); M2() -> (); }; };
ABI Compatibility: YES
[Selector]
to retain compatibility:protocol P {
[Selector= "M1"]
M1_new() -> ();
M2() -> ();
};
Transition Considerations: NO
protocol P { protocol P { M1() -> (); M1() -> (); M2() -> (); M2() -> (); M3() -> (); }; };
ABI Compatibility: YES
Transition Considerations: YES
[Transitional]
to the new member:protocol P {
M1() -> ();
M2() -> ();
[Transitional="msg"]
M3() -> ();
}
protocol P { protocol P { M1() -> (); M1() -> (); M2() -> (); }; };
ABI Compatibility: YES
Transition Considerations: YES
[Transitional]
to M2()
.fidl
Follow the same rules as structs.