Skip to content
Merged
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
67 changes: 67 additions & 0 deletions .claude/rules/technical-writing.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
---
paths:
- "**/*.go"
- "**/*.md"
---

# Technical writing style (go-openapi)

Applies to every committed comment, commit message, README and doc-site page.

The standard is Ernest Gowers, *Plain Words*: **be short, be simple, be human.**
His worked example is the whole rule:

DON'T Was this the realisation of an anticipated liability?
DO Did you expect to have to do this?

The abstract nouns carry no information; the concrete verb carries all of it.

## Two tests

**The grep test.** Does the sentence contain something a reader can search for — an
identifier, a file, a flag, an error, a number with a unit? Prose that names nothing has
described the code without pointing at it.

**The quotability test.** A sentence that would survive being quoted on its own is too
pleased with itself. Rewrite it until it merely sounds true.

## Never define by inversion

The worst and most frequent fault. A copula whose subject or predicate is a wh-clause
promises a definition and delivers a metaphor. Both directions are banned:

DON'T Coverage is what says which templates a suite never reaches.
DON'T What is lost is the doc comment.
DO Coverage records which templates the suite never executed.
DO A synthesized type loses its doc comment.

The rewrite is mechanical: find the verb hiding inside the wh-clause and make it the main
verb of the sentence.

`which is why` pointing back at a fact just stated is legitimate, and rationed — one per
comment is plenty.

## The rest

- **Name the thing.** `WithRoots`, not "the option that scopes a repository". Name the
error, the file, the flag, the upstream package, the constant.
- **Statement, not aphorism.** State mechanism and effect. Never close a paragraph on a
maxim: the reflex lands hardest on a closing sentence.
- **Keep a subject.** "New returns an error if the source is unreadable", not "What a
source leaves out is settled where it is declared".
- **Plain verbs.** add, fix, return, parse, reject, cap, prune, record. Code does not say,
judge, grant, refuse, know, mean to, or reach for. `report` is fine when something
genuinely reports.
- **Keep the numbers.** Sizes with units, counts, ratios, advisory ids. `286 -> 178 KiB`,
`GHSA-v2xp-g8xf-22pf`. Dropping them for a smoother sentence loses information.
- **Be human.** Address the reader where there is advice: "Use `WithRoot` to confine local
loading." Admit the awkward thing rather than smoothing it over.

## Self-check

# definition by inversion, both directions
grep -rnE '\b(is|are) (what|where) [a-z]' --include='*.go' --include='*.md' .
grep -rnE '(^|\. )What [a-z][a-z ,-]{3,50} (is|are) ' --include='*.go' --include='*.md' .

Subtract the legitimate `which/that/this/it is what` before judging the first one.
Neither grep is a verdict — they find one fault out of six. The others need reading.
2 changes: 1 addition & 1 deletion docs/doc-site/usage/core/validation.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,4 +79,4 @@ named formats (`date-time`, `uuid`, `email`, …) into the validator. You
rarely build one by hand — the server's `*Context` and the client `Runtime`
each carry one and pass it down. To register a custom format
(`x-go-type` style), call `strfmt.Default.Add(...)` once at startup; the
default registry is what both sides use unless overridden.
both sides use the default registry unless overridden.
2 changes: 1 addition & 1 deletion docs/examples/auth/bearerjwt/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ import (
// doc stands in for a real `*loads.Document` loaded via `loads.Spec`.
var doc *loads.Document

// principal is what the authenticator returns on success. The runtime
// principal is the value the authenticator returns on success. The runtime
// stores it in the request context for the operation handler to use.
type principal struct {
Subject string
Expand Down
2 changes: 1 addition & 1 deletion docs/examples/client/intro/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
// cannot rot silently.
//
// `go run .` exercises the demo (the SubmitContext call is expected to
// fail against the placeholder host — wiring is what we demonstrate).
// fail against the placeholder host — this demonstrates the wiring).
package main

import (
Expand Down
2 changes: 1 addition & 1 deletion docs/examples/server/security/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@ func registerAuthorized() {
func readPrincipal(r *http.Request) {
if r == nil {
// readPrincipal is invoked from main() for compile coverage; the
// snippet body itself is what gets rendered into the docs.
// snippet body itself is rendered into the docs.
return
}

Expand Down
Loading