Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
88 changes: 87 additions & 1 deletion api-guidelines/Sources/APIGuidelines.docc/Documentation.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,91 @@
# ``APIGuidelines``

Design Swift APIs that prioritize clarity at the point of use through effective naming and consistent conventions.

@Metadata {
@DisplayName("API Guidelines")
}
}

## Overview

Delivering a clear, consistent developer experience when writing Swift code is largely defined by the names and idioms that appear in APIs.
These design guidelines explain how to make sure that your code feels like a part of the larger Swift ecosystem.

* **Clarity at the point of use** is your most important goal.
Entities such as methods and properties are declared only once but
*used* repeatedly. Design APIs to make those uses clear and
concise. When evaluating a design, reading a declaration is seldom
sufficient; always examine a use case to make sure it looks
clear in context.

* **Clarity is more important than brevity.** Although Swift
code can be compact, it is a *non-goal*
to enable the smallest possible code with the fewest characters.
Brevity in Swift code, where it occurs, is a side-effect of the
strong type system and features that naturally reduce boilerplate.

* **Write a documentation comment**
for every declaration. Insights gained by writing documentation can
have a profound impact on your design, so don't put it off.

> Warning:
> If you are having trouble describing your API's
> functionality in simple terms, **you may have designed the wrong API.**

## Topics

### Fundamentals

- <doc:documentation-comments>

### Naming — Promote Clear Usage

- <doc:avoid-ambiguity>
- <doc:omit-needless-words>
- <doc:name-according-to-roles>
- <doc:weak-type-information>

### Naming — Strive for Fluent Usage

- <doc:grammatical-phrases>
- <doc:factory-methods>
- <doc:initializer-first-arguments>
- <doc:side-effect-naming>
- <doc:boolean-assertions>
- <doc:protocol-nouns>
- <doc:protocol-capability-suffixes>
- <doc:noun-names>

### Naming — Use Terminology Well

- <doc:avoid-obscure-terms>
- <doc:established-meaning>
- <doc:avoid-abbreviations>
- <doc:embrace-precedent>

### Conventions — General

- <doc:computed-property-complexity>
- <doc:methods-over-free-functions>
- <doc:case-conventions>
- <doc:shared-base-names>

### Conventions — Parameters

- <doc:parameter-names-for-documentation>
- <doc:defaulted-parameters>
- <doc:default-parameter-order>
- <doc:prefer-fileid>

### Conventions — Argument Labels

- <doc:indistinguishable-arguments>
- <doc:value-preserving-conversions>
- <doc:prepositional-phrase-labels>
- <doc:grammatical-phrase-arguments>
- <doc:label-other-arguments>

### Special Instructions

- <doc:tuple-and-closure-labels>
- <doc:unconstrained-polymorphism>
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# Avoid abbreviations

Avoid abbreviations.

## Overview

Abbreviations, especially non-standard ones, are effectively terms-of-art, because understanding depends on correctly translating them into their non-abbreviated forms.

> The intended meaning for any abbreviation you use should be
> easily found by a web search.
24 changes: 24 additions & 0 deletions api-guidelines/Sources/APIGuidelines.docc/avoid-ambiguity.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Include words to avoid ambiguity

Include all the words needed to avoid ambiguity for a person reading code where the name is used.

## Overview

✅ For example, consider a method that removes the element at a
given position within a collection.

```swift
extension List {
public mutating func remove(at position: Index) -> Element
}
employees.remove(at: x)
```

⛔ If we were to omit the word `at` from the method signature, it could
imply to the reader that the method searches for and removes an
element equal to `x`, rather than using `x` to indicate the
position of the element to remove.

```swift
employees.remove(x) // unclear: are we removing x?
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# Avoid obscure terms

Avoid obscure terms if a more common word conveys meaning just as well.

## Overview

**Term of Art**
: *noun* - a word or phrase that has a precise, specialized meaning
within a particular field or profession.

Don't say "epidermis" if "skin" will serve your purpose.
Terms of art are an essential communication tool, but should only be
used to capture crucial meaning that would otherwise be lost.
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Boolean methods read as assertions

Uses of Boolean methods and properties should read as assertions about the receiver when the use is nonmutating.

## Overview

For example: `x.isEmpty`, `line1.intersects(line2)`.
24 changes: 24 additions & 0 deletions api-guidelines/Sources/APIGuidelines.docc/case-conventions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Follow case conventions

Follow case conventions.

## Overview

Names of types and protocols are `UpperCamelCase`. Everything else is `lowerCamelCase`.

[Acronyms and initialisms](https://en.wikipedia.org/wiki/Acronym)
that commonly appear as all upper case in American English should be
uniformly up- or down-cased according to case conventions:

```swift
var **utf8**Bytes: [**UTF8**.CodeUnit]
var isRepresentableAs**ASCII** = true
var user**SMTP**Server: Secure**SMTP**Server
```

Other acronyms should be treated as ordinary words:

```swift
var **radar**Detector: **Radar**Scanner
var enjoys**Scuba**Diving = true
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# Document computed property complexity

Document the complexity of any computed property that is not O(1).

## Overview

People often assume that property access involves no
significant computation, because they have stored properties as a
mental model. Be sure to alert them when that assumption may be
violated.
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Locate parameters with defaults toward the end

Prefer to locate parameters with defaults toward the end of the parameter list.

## Overview

Parameters without defaults are usually more essential to the semantics of a method, and provide a stable initial pattern of use where methods are invoked.
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
# Take advantage of defaulted parameters

Take advantage of defaulted parameters when it simplifies common uses.

## Overview

Any parameter with a single commonly-used value is a candidate for a default. Default arguments improve readability by hiding irrelevant information.

⛔ Passing every option explicitly buries the one argument that matters among values callers rarely change:

```swift
let order = lastName.compare(
royalFamilyName**, options: [], range: nil, locale: nil**)
```

✅ can become the much simpler:

```swift
let order = lastName.**compare(royalFamilyName)**
```

Default arguments are generally preferable to the use of method
families, because they impose a lower cognitive burden on anyone
trying to understand the API.

✅ A single method with defaults replaces the whole family below, at a much lower cognitive cost:

```swift
extension String {
/// *...description...*
public func compare(
_ other: String, options: CompareOptions **= []**,
range: Range<Index>? **= nil**, locale: Locale? **= nil**
) -> Ordering
}
```

⛔ The above may not be simple, but it is much simpler than:

```swift
extension String {
/// *...description 1...*
public func **compare**(_ other: String) -> Ordering
/// *...description 2...*
public func **compare**(_ other: String, options: CompareOptions) -> Ordering
/// *...description 3...*
public func **compare**(
_ other: String, options: CompareOptions, range: Range<Index>) -> Ordering
/// *...description 4...*
public func **compare**(
_ other: String, options: StringCompareOptions,
range: Range<Index>, locale: Locale) -> Ordering
}
```

Every member of a method family needs to be separately documented
and understood by users. To decide among them, a user needs to
understand all of them, and occasional surprising relationships—for
example, `foo(bar: nil)` and `foo()` aren't always synonyms—make
this a tedious process of ferreting out minor differences in
mostly identical documentation. Using a single method with
defaults provides a vastly superior programmer experience.
115 changes: 115 additions & 0 deletions api-guidelines/Sources/APIGuidelines.docc/documentation-comments.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
# Write a documentation comment for every declaration

Insights gained by writing documentation can have a profound impact on your design, so don't put it off.

## Overview

* Use Swift's [dialect of Markdown](https://docs.swift.org/latest/documentation/docc/formatting-your-documentation-content).

* Begin with a summary that describes the entity being declared.
Often, an API can be completely understood from its declaration and
its summary.

```swift
/// **Returns a "view" of `self` containing the same elements in**
/// **reverse order.**
func reversed() -> ReverseCollection<Self>
```

* Focus on the summary; it's the most important part. Many
excellent documentation comments consist of nothing more than a
great summary.

* Use a single sentence fragment if possible, ending with a
period. Do not use a complete sentence.

* Describe what a function or method *does* and what it
*returns*, omitting null effects and `Void` returns:

```swift
/// **Inserts** `newHead` at the beginning of `self`.
mutating func prepend(_ newHead: Int)

/// **Returns** a `List` containing `head` followed by the elements
/// of `self`.
func prepending(_ head: Element) -> List

/// **Removes and returns** the first element of `self` if non-empty;
/// returns `nil` otherwise.
mutating func popFirst() -> Element?
```

Note: in rare cases like `popFirst` above, the summary is formed
of multiple sentence fragments separated by semicolons.

* Describe what a subscript *accesses*:

```swift
/// **Accesses** the `index`th element.
subscript(index: Int) -> Element { get set }
```

* Describe what an initializer *creates*:

```swift
/// **Creates** an instance containing `n` repetitions of `x`.
init(count n: Int, repeatedElement x: Element)
```

* For all other declarations, describe what the declared entity *is*.

```swift
/// **A collection that** supports equally efficient insertion/removal
/// at any position.
struct List {

/// **The element at the beginning** of `self`, or `nil` if self is
/// empty.
var first: Element?
...
```

* Optionally, continue with one or more paragraphs and bullet
items. Paragraphs are separated by blank lines and use complete
sentences.

The following example shows the summary, additional discussion,
parameters section, and symbol commands:

```swift
/// Writes the textual representation of each // ← Summary
/// element of `items` to the standard output.
/// // ← Blank line
/// The textual representation for each item `x` // ← Additional discussion
/// is generated by the expression `String(x)`.
///
/// - **Parameter separator**: text to be printed // ⎫
/// between items. // ⎟
/// - **Parameter terminator**: text to be printed // ⎬ Parameters section
/// at the end. // ⎟
/// // ⎭
/// - **Note**: To print without a trailing // ⎫
/// newline, pass `terminator: ""` // ⎟
/// // ⎬ Symbol commands
/// - **SeeAlso**: `CustomDebugStringConvertible`, // ⎟
/// `CustomStringConvertible`, `debugPrint`. // ⎭
public func print<Target: OutputStreamType>(
_ items: Any..., separator: String = " ", terminator: String = "\n")
```

* Use recognized
[symbol documentation markup](https://docs.swift.org/latest/documentation/docc/writing-symbol-documentation-in-your-source-files)
elements to add information beyond the summary, whenever
appropriate.

* Know and use recognized bullet items with
[symbol command syntax](https://docs.swift.org/latest/documentation/docc/writing-symbol-documentation-in-your-source-files#Describe-the-Parameters-of-a-Method). Popular development
tools such as Xcode give special treatment to bullet items that
start with the following keywords:

| [Attention](https://docs.swift.org/latest/documentation/docc/other-formatting-options#Add-Notes-and-Other-Asides) | [Author](https://docs.swift.org/latest/documentation/docc/other-formatting-options#Add-Notes-and-Other-Asides) | [Authors](https://docs.swift.org/latest/documentation/docc/other-formatting-options#Add-Notes-and-Other-Asides) | [Bug](https://docs.swift.org/latest/documentation/docc/other-formatting-options#Add-Notes-and-Other-Asides) |
| [Complexity](https://docs.swift.org/latest/documentation/docc/other-formatting-options#Add-Notes-and-Other-Asides) | [Copyright](https://docs.swift.org/latest/documentation/docc/other-formatting-options#Add-Notes-and-Other-Asides) | [Date](https://docs.swift.org/latest/documentation/docc/other-formatting-options#Add-Notes-and-Other-Asides) | [Experiment](https://docs.swift.org/latest/documentation/docc/other-formatting-options#Add-Notes-and-Other-Asides) |
| [Important](https://docs.swift.org/latest/documentation/docc/other-formatting-options#Add-Notes-and-Other-Asides) | [Invariant](https://docs.swift.org/latest/documentation/docc/other-formatting-options#Add-Notes-and-Other-Asides) | [Note](https://docs.swift.org/latest/documentation/docc/other-formatting-options#Add-Notes-and-Other-Asides) | [Parameter](https://docs.swift.org/latest/documentation/docc/writing-symbol-documentation-in-your-source-files#Describe-the-Parameters-of-a-Method) |
| [Parameters](https://docs.swift.org/latest/documentation/docc/writing-symbol-documentation-in-your-source-files#Describe-the-Parameters-of-a-Method) | [Postcondition](https://docs.swift.org/latest/documentation/docc/other-formatting-options#Add-Notes-and-Other-Asides) | [Precondition](https://docs.swift.org/latest/documentation/docc/other-formatting-options#Add-Notes-and-Other-Asides) | [Remark](https://docs.swift.org/latest/documentation/docc/other-formatting-options#Add-Notes-and-Other-Asides) |
| [Requires](https://docs.swift.org/latest/documentation/docc/other-formatting-options#Add-Notes-and-Other-Asides) | [Returns](https://docs.swift.org/latest/documentation/docc/writing-symbol-documentation-in-your-source-files#Describe-the-Return-Value-of-a-Method) | [SeeAlso](https://docs.swift.org/latest/documentation/docc/other-formatting-options#Add-Notes-and-Other-Asides) | [Since](https://docs.swift.org/latest/documentation/docc/other-formatting-options#Add-Notes-and-Other-Asides) |
| [Throws](https://docs.swift.org/latest/documentation/docc/writing-symbol-documentation-in-your-source-files#Describe-the-Thrown-Errors-of-a-Method) | [ToDo](https://docs.swift.org/latest/documentation/docc/other-formatting-options#Add-Notes-and-Other-Asides) | [Version](https://docs.swift.org/latest/documentation/docc/other-formatting-options#Add-Notes-and-Other-Asides) | [Warning](https://docs.swift.org/latest/documentation/docc/other-formatting-options#Add-Notes-and-Other-Asides) |
24 changes: 24 additions & 0 deletions api-guidelines/Sources/APIGuidelines.docc/embrace-precedent.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Embrace precedent

Embrace precedent.

## Overview

Don't optimize terms for the total beginner at the expense of conformance to existing culture.

It is better to name a contiguous data structure `Array` than to
use a simplified term such as `List`, even though a beginner
might grasp the meaning of `List` more easily. Arrays are
fundamental in modern computing, so every programmer knows—or
will soon learn—what an array is. Use a term that most
programmers are familiar with, and their web searches and
questions will be rewarded.

Within a particular programming *domain*, such as mathematics, a
widely precedented term such as `sin(x)` is preferable to an
explanatory phrase such as
`verticalPositionOnUnitCircleAtOriginOfEndOfRadiusWithAngle(x)`.
Note that in this case, precedent outweighs the guideline to
avoid abbreviations: although the complete word is `sine`,
"sin(*x*)" has been in common use among programmers for decades,
and among mathematicians for centuries.
Loading