| # FIDL design principles |
| |
| 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: |
| |
| 1. Users (using a Fuchsia product) |
| 2. End-developers (using FIDL bindings) |
| 3. Fuchsia contributors (using FIDL bindings) |
| 4. API designers (authoring FIDL libraries) |
| 5. [Fuchsia FIDL Team] members |
| |
| This list was adapted from that of the [API Council Charter]. |
| |
| ## ABI first, API second |
| |
| From [RFC-0050: Syntax revamp][rfc-0050-principles]: |
| |
| > FIDL is primarily concerned with defining Application Binary Interface (ABI) |
| > concerns, and second with Application Programming Interface (API) concerns. |
| |
| ## Binary wire format first {#binary-wire-format-first} |
| |
| From [RFC-0050: Syntax revamp][rfc-0050-binary-wire-format-first]: |
| |
| > While many formats can represent FIDL messages, the [FIDL Wire |
| > Format][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. |
| |
| ## Fewest features |
| |
| From [RFC-0050: Syntax revamp][rfc-0050-fewest-features]: |
| |
| > 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 {#you-only-pay} |
| |
| From [RFC-0027: You only pay for what you use][rfc-0027]: |
| |
| > 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][rfc-0047-motivation] 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][rfc-0061-pros-and-cons] 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][rfc-0056], 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][rfc-0032] 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][rfc-0029-breakage-at-a-distance] |
| discusses this problem as it relates to protocol composition. [RFC-0048: |
| Explicit union ordinals][rfc-0048-hashing-only-for-protocols] revisits the |
| topic, explaining why FIDL only uses hashing for protocols. |
| |
| [RFC-0057: Default no handles][rfc-0057] introduced a distinction between [value |
| and resource types][lang-resource]. 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-lint` or `fidl-format` rather than in `fidlc`. |
| |
| <!-- TODO(fxbug.dev/74753): Say "the linter enforces an ordering" when it is true. --> |
| 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][rfc-0040] fixed the |
| problem of identifiers colliding after case transformation by having `fidlc` |
| 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 `snake_case` |
| methods are strongly preferred. |
| |
| ## Canonical representation |
| |
| The FIDL wire format [is canonical][wire-format-dual-forms]: 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. |
| |
| For example, the [specification][wire-format-padding] requires that all padding |
| bytes are zero. Similarly, [RFC-0047: Tables][rfc-0047-wire-format] disallows |
| storing extraneous empty envelopes to ensure a canonical representation. |
| |
| 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][lang-resource], it is a simple `memcmp`. |
| |
| ## No allocations required |
| |
| From the [wire format specification][wire-format-dual-forms]: |
| |
| > 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"](#you-only-pay), |
| in that it caters to very low-level uses of FIDL where `malloc` may not yet |
| exist, or is prohibitively expensive. |
| |
| ## Transport generality |
| |
| While [the binary wire format comes first](#binary-wire-format-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][rfc-0050-transport-generalization] describes the |
| future direction for transport generalization. |
| |
| [RFC-0062: Method impossible][rfc-0062] was rejected in part because it coupled |
| FIDL too closely to the Zircon channel transport. |
| |
| <!-- link labels --> |
| [API Council Charter]: /docs/contribute/governance/api_council.md#values |
| [Fuchsia FIDL Team]: /src/fidl/OWNERS |
| [lang-resource]: /docs/reference/fidl/language/language.md#value-vs-resource |
| [wire-format]: /docs/reference/fidl/language/wire-format |
| [wire-format-dual-forms]: /docs/reference/fidl/language/wire-format#dual-forms |
| [wire-format-padding]: /docs/reference/fidl/language/wire-format#padding |
| [rfc-0027]: /docs/contribute/governance/rfcs/0027_you_only_pay_what_you_use.md |
| [rfc-0029-breakage-at-a-distance]: /docs/contribute/governance/rfcs/0029_increasing_method_ordinals.md#breakage-at-a-distance |
| [rfc-0029]: /docs/contribute/governance/rfcs/0029_increasing_method_ordinals.md |
| [rfc-0032]: /docs/contribute/governance/rfcs/0032_efficient_envelopes.md |
| [rfc-0040]: /docs/contribute/governance/rfcs/0040_identifier_uniqueness.md |
| [rfc-0047-motivation]: /docs/contribute/governance/rfcs/0047_tables.md#motivation |
| [rfc-0047-wire-format]: /docs/contribute/governance/rfcs/0047_tables.md#wire-format |
| [rfc-0048-hashing-only-for-protocols]: /docs/contribute/governance/rfcs/0048_explicit_union_ordinals.md#hashing-only-for-protocols |
| [rfc-0050-binary-wire-format-first]: /docs/contribute/governance/rfcs/0050_syntax_revamp.md#binary-wire-format-first |
| [rfc-0050-fewest-features]: /docs/contribute/governance/rfcs/0050_syntax_revamp.md#fewest-features |
| [rfc-0050-principles]: /docs/contribute/governance/rfcs/0050_syntax_revamp.md#principles |
| [rfc-0050-transport-generalization]: /docs/contribute/governance/rfcs/0050_syntax_revamp.md#transport-generalization |
| [rfc-0056]: /docs/contribute/governance/rfcs/0056_empty_structs.md |
| [rfc-0057]: /docs/contribute/governance/rfcs/0057_default_no_handles.md |
| [rfc-0061-pros-and-cons]: /docs/contribute/governance/rfcs/0061_extensible_unions.md#pros-and-cons |
| [rfc-0062]: /docs/contribute/governance/rfcs/0062_method_impossible.md |