SKILL.md
$2c
Argument labels determine how a call site reads. Apply these rules in order.
When to omit the first argument label
Grammatical phrase rule. When the first argument forms a grammatical phrase with the base name, omit the label. Move any leading words from what would be the label into the base name instead.
// GOOD — reads as "add subview y"
view.addSubview(y)
// BAD — redundant label breaks the phrase
view.add(subview: y)
Value-preserving type conversions. When an initializer performs a value-preserving (widening) conversion, omit the first argument label.
// GOOD — widening conversion, no label
let value = Int64(someUInt32)
let str = String(someCharacter)
// Narrowing or lossy conversions keep a label
let approx = Int64(truncating: someDecimal)
let str = String(describing: someObject)
Indistinguishable arguments. When all arguments cannot be usefully distinguished, omit all labels.
// GOOD — arguments are peers
let smaller = min(x, y)
zip(sequence1, sequence2)
When to use a prepositional label
Prepositional phrase rule. When the first argument completes a prepositional phrase with the base name, label it with the preposition.
// GOOD — "remove boxes having length 12"
x.removeBoxes(havingLength: 12)
// GOOD — "fade from red"
view.fade(from: red)
// GOOD — "relative path from root"
path.relativePath(from: root)
Exception — abstraction boundary. When the first two arguments represent parts of a single abstraction, fold the preposition into the base name so each component gets its own label.
// GOOD — x and y are parts of a single abstraction (a point)
a.moveTo(x: b, y: c)
// BAD — preposition attaches to first arg, leaving y unlabeled
a.move(toX: b, y: c)
Default: label everything else
When no special rule above applies, label the argument.
// GOOD
array.split(maxSplits: 2)
button.setTitle("OK", for: .normal)
controller.dismiss(animated: true)
array.sorted(by: >)
Argument label decision table
Situation
Rule
Example
First arg completes grammatical phrase
Omit label, merge words into base name
addSubview(y)
Value-preserving init conversion
Omit first label
Int64(someUInt32)
Arguments are indistinguishable peers
Omit all labels
min(x, y)
First arg completes prepositional phrase
Label with preposition
fade(from: red)
First two args form a single abstraction
Fold preposition into base name
moveTo(x: b, y: c)
Everything else
Label it
split(maxSplits: 2)
For extended examples and edge cases, see references/argument-labels-and-parameters.md.
Side-Effect Naming
Name functions and methods by their side effects.
Functions with side effects — imperative verbs
When a function mutates state, name it as an imperative verb phrase.
// Mutates — imperative verb
array.sort()
array.append(newElement)
list.remove(at: index)
timer.invalidate()
Functions without side effects — nouns or adjective phrases
When a function returns a result without mutating anything, name it as a noun phrase, adjective phrase, or read as a description of what it returns.
// Pure — noun/description
let d = point.distance(to: origin)
let area = rect.intersection(other)
let line = text.trimmingCharacters(in: .whitespaces)
Boolean properties and methods
Boolean properties and methods read as assertions about the receiver.
// GOOD — reads as "line is empty"
line.isEmpty
set.contains(element)
url.isFileURL
// BAD — not an assertion
line.empty // verb? adjective?
set.includes // incomplete phrase
For more examples, see references/side-effects-and-mutating-pairs.md.
Mutating and Nonmutating Pairs
When an operation has both mutating and nonmutating variants, name them as a pair.
Verb-described operations — -ed/-ing suffix
When the operation is naturally described by a verb:
- Mutating: imperative verb (
sort,append,reverse)
- Nonmutating: past participle
-edor present participle-ing
Default to -ed (past participle). When -ed is ungrammatical — typically when the verb does not form a natural past participle, or when adding -ed produces an awkward phrase — use -ing (present participle) instead.
Mutating
Nonmutating
Why
sort()
sorted()
-ed — "a sorted array"
reverse()
reversed()
-ed — "a reversed collection"
append(y)
appending(y)
-ing — "appended" is ungrammatical here
stripNewlines()
strippingNewlines()
-ing — "stripped newlines" is awkward
Noun-described operations — form- prefix
When the operation is naturally described by a noun:
- Nonmutating: the noun itself (
union,intersection)
- Mutating:
formprefix (formUnion,formIntersection)
// Nonmutating — returns new value
let combined = a.union(b)
// Mutating — modifies in place
a.formUnion(b)
Factory methods — make- prefix
Factory methods that create a new value start with make.
let iterator = collection.makeIterator()
let buffer = parser.makeBuffer()
Pair decision table
Operation described by
Mutating name
Nonmutating name
Example pair
Verb (default)
verb
verb + -ed
sort() / sorted()
Verb (-ed is ungrammatical)
verb
verb + -ing
stripNewlines() / strippingNewlines()
Noun
form + Noun
noun
formUnion(b) / union(b)
For the full -ed/-ing decision tree and stdlib examples, see references/side-effects-and-mutating-pairs.md.
Documentation Comments
Every public declaration must have a documentation comment.
Summary rules by declaration kind
Declaration
Summary describes
Function / method
What it does and what it returns
Subscript
What it accesses
Initializer
What it creates
Type / property / variable
What it is
Write summaries as a single sentence fragment, beginning with a verb (for actions) or a noun phrase (for entities), ending in a period.
/// Returns the element at the specified index.
func element(at index: Int) -> Element { ... }
/// The number of elements in the collection.
var count: Int { ... }
/// Creates a new array with the given elements.
init(_ elements: some Sequence<Element>) { ... }
/// Accesses the element at the specified position.
subscript(index: Int) -> Element { ... }
Symbol markup
Use standard symbol markup after the summary when relevant:
- Parameter name:for individual parameters
- Parameters:block for multiple parameters
- Returns:for the return value
- Throws:for errors thrown
- Complexity:for algorithmic complexity
/// Removes and returns the element at the specified position.
///
/// - Parameter index: The position of the element to remove.
/// - Returns: The removed element.
/// - Complexity: O(*n*), where *n* is the length of the collection.
mutating func remove(at index: Int) -> Element { ... }
O(1) complexity rule
Document the complexity of any computed property that is not O(1). Callers assume properties are O(1) by default. If a property does more than constant-time work, state the complexity explicitly.
/// The total weight of all items.
///
/// - Complexity: O(*n*), where *n* is the number of items.
var totalWeight: Double {
items.reduce(0) { $0 + $1.weight }
}
For documentation patterns and examples, see references/conventions-and-special-rules.md.
Clarity and Naming
Clarity at the point of use is the most important goal. Every design decision serves the person reading a call site.
Clarity over brevity. Longer names are acceptable when they remove ambiguity. Do not abbreviate.
// GOOD
employees.remove(at: position)
// BAD — ambiguous: remove the element? remove at position?
employees.remove(position)
Include words needed to avoid ambiguity. If omitting a word makes the call site unclear, keep it.
// GOOD — "at" clarifies the argument's role
friends.remove(at: index)
// BAD — is "index" the element to remove or the position?
friends.remove(index)
Omit needless words. Do not repeat type information already available from the context.
// GOOD
allViews.remove(cancelButton)
// BAD — "Element" repeats the type
allViews.removeElement(cancelButton)
Name variables and parameters by role, not type. Use the entity's role in the current context, not its type name.
// GOOD — describes the role
var greeting: String
func add(_ observer: NSObject, for keyPath: String)
// BAD — names the type
var string: String
func add(_ object: NSObject, for string: String)
Compensate for weak type information. When a parameter type is Any, AnyObject, or a fundamental type like Int or String, add role-clarifying words to the name.
// GOOD — role is clear despite weak types
func addObserver(_ observer: NSObject, forKeyPath path: String)
// BAD — what does "string" mean here?
func add(_ object: NSObject, for string: String)
For extended naming examples and patterns, see references/naming-and-clarity.md.
Fluent Usage and Protocols
Call sites read as grammatical English. Prefer names that form grammatical phrases at the point of use.
// GOOD — reads fluently
x.insert(y, at: z) // "x, insert y at z"
x.subviews.remove(at: i) // "x's subviews, remove at i"
x.makeIterator() // "x, make iterator"
// BAD — ungrammatical
x.insert(y, position: z)
x.subviews.remove(i)
Initializer first argument. The first argument to an initializer should not form a phrase continuing the type name.
// GOOD
let foreground = Color(red: 32, green: 64, blue: 128)
// BAD — "Color with red" reads awkwardly
let foreground = Color(havingRGBValuesRed: 32, green: 64, blue: 128)
Protocol naming conventions:
Protocol describes
Naming pattern
Examples
What something is
Noun
Collection, IteratorProtocol
A capability
-able, -ible, or -ing suffix
Equatable, Hashable, Sendable
General Conventions
Casing. Types and protocols use UpperCamelCase. Everything else uses lowerCamelCase. Acronyms that are commonly all-caps in American English appear uniformly upper- or lower-cased based on position.
var utf8Bytes: [UTF8.CodeUnit]
var isRepresentableAsASCII = true
var userSMTPServer: SMTPServer
Methods and properties over free functions. Prefer methods and properties. Use free functions only when:
- There is no obvious
self—min(x, y)
- The function is an unconstrained generic —
print(value)
- The function syntax is established domain notation —
sin(x)
Default arguments over method families. Prefer a single method with default parameters over a family of methods that differ only in which parameters they accept. Place defaulted parameters at the end. Parameters with default values should always have argument labels — defaulted parameters are usually omitted at call sites, so their labels must be clear when they do appear.
// GOOD — labeled with defaults
func decode(_ data: Data, encoding: String.Encoding = .utf8) -> String?
// BAD — method family
func decode(_ data: Data) -> String?
func decode(_ data: Data, encoding: String.Encoding) -> String?
Overload safety. Methods may share a base name when they operate in different type domains or when their meaning is clear from context. Avoid return-type-only overloads that cause ambiguity at the call site.
For casing edge cases, overload patterns, and tuple/closure naming, see references/conventions-and-special-rules.md.
Common Mistakes
-
Omitting needed argument labels. Using remove(position) instead of remove(at: position) when the role of the argument is ambiguous without the label.
-
Using -ed when -ing is correct. Applying stripped() when the past participle is ungrammatical — use stripping() instead. Test: does "a [verb]-ed [noun]" read naturally?
-
Using verb names for side-effect-free operations. Naming a nonmutating method sort() that returns a new collection — use sorted() to signal no mutation.
-
Naming by type instead of role. Using string instead of greeting, or array instead of elements, when the role would be more informative.
-
Missing documentation comments. Leaving public declarations undocumented, or writing summaries that describe the implementation rather than the purpose.
-
Not documenting non-O(1) computed properties. Exposing a linear-time computed property without a Complexity: note, causing callers to assume O(1) and use it in loops.
-
Applying form- prefix to verb-based operations. Writing formSort() instead of just sort() — the form prefix is only for noun-based operations (formUnion).
-
Factory methods without make- prefix. Naming factory methods as createIterator() or buildBuffer() instead of makeIterator() and makeBuffer().
-
Repeating type information in names. Writing removeElement(cancelButton) or stringValue: String when the type is already evident from context.
-
Return-type-only overloads. Defining overloads that differ only in return type, creating ambiguity when the compiler cannot infer the expected type.
-
Unlabeled tuple members and closure parameters. Exposing tuples or closures in public API without naming their components, forcing callers to use positional access.
Review Checklist
Argument Labels
- First argument follows the correct label rule (grammatical phrase, prepositional, conversion, or labeled)
- Prepositional labels do not incorrectly group independent arguments
- Value-preserving conversion initializers omit the first label
- All non-special-case arguments have labels
Naming Semantics
- Mutating methods use imperative verb form
- Nonmutating methods use -ed/-ing or noun form
- Mutating/nonmutating pairs follow the correct pattern (verb pair or noun/form-noun pair)
- Boolean properties read as assertions (
isEmpty,isValid,contains)
- Variables and parameters are named by role, not type
Documentation
- Every public declaration has a doc comment
- Summaries are single sentence fragments ending in a period
- Summaries describe the correct thing per declaration kind (action, access, creation, entity)
- Non-O(1) computed properties document their complexity
- Parameters, return values, and thrown errors are documented with symbol markup
Conventions
- Types and protocols use UpperCamelCase; everything else uses lowerCamelCase
- Acronyms are uniformly cased based on position
- Default arguments are preferred over method families
- Overloads do not differ only in return type
- Protocol names follow the noun (is-a) or suffix (capability) convention
References
- Naming clarity, role-based naming, weak-type compensation, and terminology: references/naming-and-clarity.md
- Argument label edge cases, parameter naming, and default argument strategy: references/argument-labels-and-parameters.md
- Side-effect naming examples, -ed/-ing decision tree, form- prefix patterns, and factory methods: references/side-effects-and-mutating-pairs.md
- Casing edge cases, complexity documentation, overload safety, tuple/closure naming, and free function exceptions: references/conventions-and-special-rules.md