This page summarizes the key design principles that FIDL has adopted over time.
Priority of constituencies
FIDL aims to respect the following priority of constituencies:
- Users (using a Fuchsia product)
- End-developers (using FIDL bindings)
- Fuchsia contributors (using FIDL bindings)
- API designers (authoring FIDL libraries)
- Fuchsia FIDL Team members
This list was adapted from that of the API Council Charter.
ABI first, API second
From RFC-0050: Syntax revamp:
FIDL is primarily concerned with defining Application Binary Interface (ABI) concerns, and second with Application Programming Interface (API) concerns.
Binary wire format first
From RFC-0050: Syntax revamp:
While many formats can represent FIDL messages, the FIDL Wire Format (or "FIDL Binary Wire Format") is the one which has preferential treatment, and is catered to first ... we choose to over rotate towards the binary ABI format when making syntax choices.
From RFC-0050: Syntax revamp:
We strive to have the fewest features and rules, and aim to combine features to achieve use cases. In practice, when considering new features, we should first try to adapt or generalize other existing features rather than introduce new features.
You only pay for what you use
When adding functionality to FIDL, we should evaluate the costs that adding that functionality imposes on people who use FIDL but do not use the new functionality. We should then have a very high bar for accepting functionality that imposes costs on people who do not use the functionality.
For example, RFC-0047: Tables followed this principle by adding tables to the language rather than replacing structs:
Tables are necessarily more complicated than structs, and so processing them will be slower and serializing them will use more space. As such, it's preferred to keep structs as is and introduce something new.
In contrast, RFC-0061: Extensible unions reached the opposite decision of replacing static unions with extensible unions, but only after a careful analysis of the tradeoffs. Unlike with tables, the extra cost imposed by extensible unions is marginal or nonexistent in most cases.
Solve real problems
We design FIDL to solve real problems and address real needs, not imagined ones. We avoid designing a "solution in search of a problem".
For example, FIDL initially did not support empty structs because it was unclear how to represent them in C/C++. In RFC-0056: Empty structs, we saw users were employing workarounds and recognized the need for an official solution. Only then did we add empty structs to the language.
Optimize based on data
Optimizing without data is useless at best and dangerous at worst. When designing optimizations (e.g. performance, binary size), we follow the data.
For example, RFC-0032: Efficient envelopes was initially accepted, but later rejected. In hindsight, it should never have been accepted because there was no data to back it up.
No breakage at a distance
We strive to avoid breakage at a distance. Changes in one place should not
cause surprising breakage in a faraway place. For example, it would be
surprising if adding a struct named
Foo to a FIDL file broke compilation
because another FIDL file in a completely different part of the codebase already
had a type named
Foo. This is why FIDL, like most programming languages, uses
namespaces to limit the scope of name collisions.
RFC-0029: Increasing method ordinals discusses this problem as it relates to protocol composition. RFC-0048: Explicit union ordinals revisits the topic, explaining why FIDL only uses hashing for protocols.
RFC-0057: Default no handles introduced a distinction between value
and resource types. One motivation for this was providing the
Clone trait in Rust for types without handles without breakage at a distance:
Although FIDL bindings can conditionally enable code based on the presence of handles, doing so is undesirable because it breaks evolvability guarantees. For example, adding a field to a table is normally safe, but adding a handle field would become source-breaking — not only for that table, but for all types transitively containing it.
Liberal syntax, idiomatic style
We do not rigidly adhere to a "one way to do it" philosophy. When we are
concerned that users will waste time deciding between trivial alternatives, we
introduce restrictions in
fidl-format rather than in
For example, FIDL accepts modifier keywords in any order, but we intend to enforce a consistent ordering in the linter.
As another example, RFC-0040: Identifier uniqueness fixed the
problem of identifiers colliding after case transformation by having
report an error if any two identifiers have the same canonical form. A simpler
alternative would have been to enforce FIDL naming conventions in the compiler.
However, this goes a step too far. There are valid reasons for using different
naming styles, for example in describing the Kernel API, where
methods are strongly preferred.
The FIDL wire format is canonical: there is exactly one encoding for a given message. As a corollary, every byte is accounted for: there is no byte that can be changed without altering the message's meaning.
A canonical representation makes FIDL simpler and more secure. For example,
allowing nonzero padding could result in FIDL messages leaking sensitive
information that happened to occupy those bytes in memory. Allowing multiple
representations for a given message also leads to rarely executed code paths
that can hide bugs, e.g. the "extra empty envelopes" code path. A canonical
representation also makes it easy to compare messages for equality without
knowing the schema: for value types, it is a simple
No allocations required
From the wire format specification:
FIDL is designed such that encoding and decoding of messages can occur in place in memory.
This requirement significantly influences the design of the wire format: you must be able to decode in place using only the stack. It is the reason the wire format uses presence indicators and a depth-first traversal order rather than, for example, and offset-based format that requires auxiliary data structures to keep track of information while decoding.
This principle is related to "You only pay for what you use",
in that it caters to very low-level uses of FIDL where
malloc may not yet
exist, or is prohibitively expensive.
While the binary wire format comes first, this does not mean FIDL should be tightly coupled to the Zircon channel transport. There are other important use cases to consider, such as describing the Kernel API, in-process messaging, and persistence.
RFC-0050: Syntax revamp describes the future direction for transport generalization.
RFC-0062: Method impossible was rejected in part because it coupled FIDL too closely to the Zircon channel transport.