From efceea64cb393aeaec8c2af7e656beae01378ea4 Mon Sep 17 00:00:00 2001 From: Joshua Bell Date: Sun, 21 Jan 2024 10:08:27 -0800 Subject: [PATCH] Document coding conventions and best practices for spec maintainers As suggested by @wchao1115 in #499 and #518, create a doc capturing past WG disscussions in the telecons, issues, and PRs, so that we can be consistent going forward. --- CONTRIBUTING.md | 2 + SpecCodingConventions.md | 121 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 123 insertions(+) create mode 100644 SpecCodingConventions.md diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index af39787a..336b3dc2 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -23,6 +23,8 @@ Wording change does not require opening a GitHub Issue, as the change in the pul Similarly, a stylistic change does not necessarily require opening a GitHub Issue. It does, however, require buy-ins from the Working Group to proceed. The best way to propose this type of change is to attend one of the bi-weekly Web Machine Learning Working Group teleconference calls. A practical way to reach out to the Working Group to get invited to the teleconference call is to post a GitHub Issue giving a rough explanation of the proposed change and ask to be invited. +Follow the guidance in [SpecCodingConventions.md](SpecCodingConventions.md) for your change to ensure it aligns with best practices and existing conventions. + Bug fixes and new content changes should proceed as follows: 1. **Open an issue in GitHub Issues** with a brief description of the problem and a potential solution if it's not already obvious. A proposal or suggestion for improvement may need a bit more explanation with possible references to related information. An active issue is the best way to get attention. Members of the Working Group scan active issues constantly. 2. **Prepare the change in a pull request** and put a reference to the active issue(s) the change is addressing in the description. We prefer that a pull request is represented by a single type of change as outlined in the previous section for a speedy review and approval. Conversely, a specific change should also be captured by a single and not multiple pull requests. This helps to reduce the dependency between pull requests and the chance for the specification to be left in a transient state between multiple pull requests. Exceptions to this should be discussed and approved by the Working Group in one of our bi-weekly calls. diff --git a/SpecCodingConventions.md b/SpecCodingConventions.md new file mode 100644 index 00000000..24c5c57c --- /dev/null +++ b/SpecCodingConventions.md @@ -0,0 +1,121 @@ +# Coding Conventions + +This document captures the WebNN spec repo conventions. The intended audience is editors and contributors to the specification. + +## Resources + +* [Writing Procedural Specs](https://garykac.github.io/procspec/) +* [Writing Specifications with Bikeshed](https://dlaliberte.github.io/bikeshed-intro/) +* [Bikeshed Docs](https://speced.github.io/bikeshed/) + +## Background Material + +The [Web IDL Standard](https://webidl.spec.whatwg.org/) standard defines the interface between JavaScript and an implementation of the standard. Web IDL is the interface between your spec and JavaScript. 99.99% of the time your spec shouldn't mention anything related to JavaScript outside of examples. (So nothing Normative). And that 0.01% is REALLY HARD to get right, leads to security issues, etc. + +The [Infra](https://infra.spec.whatwg.org/) standard captures the fundamental concepts on which specifications are built - algorithms, primitive data types, and structures. Most Normative spec text should be written in terms of these concepts. That said, Infra is not perfect or complete. If you need to go beyond it, look at other specs for inspiration - it's better to follow precedent. Look at open Infra issues and consider filing issues if you're in novel territory. + + +### High Level Spec Authoring Guidance + +* Earlier web specs gave high level description of behavior, e.g. input and output. But it turns out a lot of the implementation can be observable and thus lead to compatibility problems. Developers don't test everywhere or read warnings. Specifications must be complete and precise, and not leave room for interpretation. +* Specs are for a specific audience: implementers; if you try to write for developers, you're gonna have a bad time. They should be looking at MDN. +* If you define anything in two places you have twice the chances of getting it wrong. If you get out of sync, everything is wrong. Prioritize precision over readability. +* That said, non-normative notes are your friend. So-called "domintro" sections help reconcile a lot of the tensions above. They're developer-facing non-normative notes in the spec. Use simpler phrasing, don't worry as much about edge cases, etc. + + +### Bikeshed Guidance + +Think of Bikeshed as a compiler for your spec. + +* Like code: the less you write the lower your chances of having a bug. +* Prefer markdown to markup - shorter, more likely to catch errors. +* Use links to terms as often as possible. This has many benefits: + * It handles formatting automatically and reduces manual work. + * It removes ambiguity about what you're describing. + * It catches errors if you spell something incorrectly or the term changes. + * If there is nothing to link to, it is a signal that your text is not precise! +* The spec build should be warning-free and error-free. ("--die-on=warning") + + +### Definitions And Linking + +* Prefer Bikeshed [autlink shortcuts](https://speced.github.io/bikeshed/#autolink-shortcuts) where possible using markdown. These do basic type checking and will style the link appropriately. The most common forms are: + * `[=term=]` to link to a term + * `[=scope/term=]` to link to a scoped term (see below) + * `{{Type}}` to link to a type defined in Web IDL + * `{{Type/property}}` to link to a member defined in Web IDL + * `[[doc]]` to link to another document. +* Do not use HTML links `..."` except as a last resort - there is usually a way to create the link using Bikeshed and some error checking. +* Where possible, prefer auto-generated IDs for terms over explicit IDs (`data-lt`). + +Example: +``` + The maximum size of a collection is... + + 1. Let |max| be the [=maximum size=] of |collection|. +``` + +* When defining subsidiary terms, like properties of an object, members of an enum, etc, scope the definitions using `dfn-for` on the `dfn` or an ancestor. + +Example: +``` + A circle is a geometric shape. + +
+ A [=circle=] has an origin and a radius. +
+ + 1. If |shape| is a [=circle=], draw it at |shape|'s [=circle/origin=]. +``` + + +### Formatting + +* Bikeshed will automatically style linked terms appropriately, for example Web IDL types show up as `code`. Try to avoid manual styling wherever possible; if you're not getting the style you expect, you may have incorrect definitions or links. +* Outside of examples, which should be appropriately styled automatically, literals such as numbers within spec prose are not JavaScript values and should not be styled as code. +* Strings used internally (e.g. operator names) should not be styled as code. + + +### Algorithms + +* Use `
` or a variant to wrap the algorithm to get special formatting and Bikeshed processing. +* Number all steps with `1.` which results in automatically incrementing numbers. This makes insertions and deletions much easier. +* Use `|pipe|` syntax for arguments and variables. Bikeshed will give warnings if a variable is only defined once in an algorithm. +* Use assertions when state within an algorithm may not be clear. +* Do not use assertions to repeat what is stated in an algorithm's declaration, e.g. types. +* Do not include conditions that can never be true. For example, if an internal algorithm will only be called with objects in a known state, do not include checks for that state that alter the flow of the algorithm. Consider assertions instead. +* Use the most specific types possible (e.g. MLOperand, not generic object). +* Use `[=this=]` to refer to the current object. +* Use `[=map/For each=] |key| → |value| of |map|` when iterating over a map, but use more specific terms for the key and value (e.g. _For each name → input of inputs:_) +* Use "let" to introduce a variable and "set" to update a variable or assign to a property. +* Use « » notation for literal lists, which helps make it clear that they are not JavaScript arrays. + + + +### Method Definitions + +* Follow the Web IDL convention for [defining methods](https://webidl.spec.whatwg.org/#method-steps), i.e. _"The operation(arg1, arg2, ...) method steps are:"_ +* The definition should be wrapped in `` and if written correctly will link to the Web IDL declaration. +* Do not include assertions about argument types. This is redundant with Web IDL declaration. +* Do not include steps that test argument types if those types are guaranteed by WebIDL. +* Do not refer to JavaScript or WebIDL types in method steps. Per the spec processing model, by the time a spec algorithm is invoked, JavaScript types (e.g. Numbers, Arrays) have been mapped to WebIDL types (e.g. unsigned longs, sequences) and those have been mapped to Infra types or general concepts (e.g. numbers, lists). +* Do not repeat detaults provided by the WebIDL declaration. +* For types like lists that can't be defaulted in WebIDL, define the default when missing as an explicit step. Example: _If options.padding does not exist, set options.padding to « 0, 0, 0, 0 »._ + + +### Internal Algorithms + +* Follow the Infra convention for [algorithm definition](https://infra.spec.whatwg.org/#algorithm-declaration), i.e. _"To [algorithm name], given a [type1] [parameter1], a [type2] [parameter2], …, perform the following steps. They return a [return type]."_ +* Bikeshed is smart enough to link verb phrases to algorithms. For example, if you define an algorithm `To screw in a lightbulb ...` you can link to it with the very readable `... after [=screwing in a lightbulb=] ...`; explicit link targets and alias text is not necessary. Use this form when possible, rather than the explicit "invoking" term. + +### Exceptions + +* Exceptions are thrown with the syntax: `[=exception/throw=] a {{TypeError}}` or `[=exception/throw=] an "{{OperationError}}" {{DOMException}}` + + +### Dictionary Members + +* Dictionary members are referenced using dotted property syntax. e.g. _options.padding_ + * Note that this is contrary to Web IDL + Infra; formally, a JavaScript object has been mapped to a Web IDL [dictionary](https://webidl.spec.whatwg.org/#idl-dictionaries) and then processed into an Infra [map](ordered) by the time a spec is using it. So formally the syntax _options["padding"]_ should be used. + +