Writing Clojure Docstrings
Clojure Docstring Guidelines
Write clear, scannable docstrings using markdown formatting. All docstrings are interpreted as Markdown on cljdoc and Codox.
Core Principles
1. Backtick-Quote Arguments and Keywords
Use backticks around function arguments and special keywords to improve readability:
(defn conj!
[coll x]
"Adds `x` to the transient collection, and return `coll`. The 'addition'
may happen at different 'places' depending on the concrete type."
,,,)
2. Link with [[Wikilinks]]
Cross-reference functions, namespaces, and Java classes using double-bracket syntax. Works in both Codex and cljdoc:
(defn unlisten!
"Removes registered listener from connection. See also [[listen!]]."
[conn key]
(swap! (:listeners (meta conn)) dissoc key))
For cross-namespace links, fully qualify:
(defn my-func
"Does something. See [[datascript.core/listen!]] for the underlying mechanism."
[conn]
,,,)
(defn query
"Executes query. See [[datascript.query]] namespace for details."
[db q]
,,,)
(defn parse-instant
"Parses timestamp string. Uses [[java.time.Instant]] internally."
[s]
,,,)
3. Include Small Usage Examples
Show the function in context with code blocks:
(defn register
"Registers a dataloader controller with the application.
Usage:
```clojure
(def app
(-> (app-state/constructor)
(dataloader/register :current-user
{:target [:user]
:loader (fn [req]
(api/get-current-user))
:params (fn [prev route]
true)})))
The dataloader will automatically fetch data when params change." [app key config] ,,,)
### 4. Document Options Maps with Tables
Use markdown tables when a function accepts an options map with multiple keys:
```clojure
(defn router
"Creates a router from raw route data and optionally options map.
Options:
| key | description
| -----------------|-------------
| `:path` | Base-path for routes
| `:routes` | Initial resolved routes (default `[]`)
| `:data` | Initial route data (default `{}`)
| `:spec` | Spec to validate route data (default `nil`)
| `:syntax` | Path parameter syntax (default `:bracket`)
| `:expand` | Function to expand route data (default `identity`)
| `:coerce` | Function to coerce parameters (default `nil`)
| `:compile` | Function to compile routes (default `identity`)
| `:conflicts` | Function to handle route conflicts (default `nil`)
Example:
```clojure
(router
[[\"api\" {:middleware [wrap-api]}]
[\"admin\" {:middleware [wrap-admin]}]]
{:path \"/v1\"})
```"
([data]
(router data nil))
([data opts]
,,,))
Docstring Structure
For public functions, docstrings should contain these fields in order:
- Complete sentence description (required)
- Paragraphs/prose/explanation with markdown sections if needed (optional, best for namespaces)
- Options enumeration using tables (required for public functions with options maps)
- Examples (optional, only when necessary—skip for obvious things)
(defn process-data
"Processes incoming data and applies transformations.
The processor validates input, applies filters based on `opts`, and
returns transformed results. Invalid data is logged and skipped.
Options:
| key | description
|-------------|-------------
| `:filters` | Vector of filter functions to apply
| `:validate` | Enable validation (default `true`)
Example:
```clojure
(process-data items {:filters [remove-nil remove-empty]
:validate true})
```"
[items opts]
,,,)
For namespaces, use markdown sections with an optional "## Related Namespaces" section at the end:
(ns myapp.data.query
"Query execution and optimization.
## Query Processing
Queries are parsed, optimized, and executed against the database.
Use [[prepare-query]] to parse and validate before execution.
## Performance
Query results are cached by default. See `:cache` option to disable.
## Related Namespaces
- [[myapp.data.schema]] - Schema definitions
- [[myapp.data.index]] - Index management")
For private functions, docstrings are optional. Add them only for complex/complicated functions, especially when defining input/output constraints:
(defn- validate-config
"Validates configuration map.
Input: Map with required keys `:host`, `:port`, `:timeout`.
Output: Validated config map or throws ExceptionInfo."
[config]
,,,)
Style Guidelines
- Start docstrings with complete sentence
- Be matter of fact and just-long-enough, not overly terse
- Skip markdown bold formatting (
**text**) entirely - Dry, sardonic humor is acceptable when it actually clarifies something (rarely)
- Focus on what the function does, not obvious implementation details
Notable Examples
Projects with exemplary docstring work:
Credit: Content adapted from 4 Small Steps Towards Awesome Clojure Docstrings by Martin Klepsch.
More from rcmerci/skills
logseq-schema
Logseq Datascript schema, built-in properties/classes, and :db/ident discovery for composing or reviewing Datascript queries about blocks/pages/tags/properties/classes. Use whenever editing or reviewing Datascript pull selectors or queries, or any code that adds/removes attributes in pull patterns, or touches property namespaces/identifiers, or requires reasoning about property value shapes/ref/cardinality in Logseq.
40logseq-cli
Operate the current Logseq command-line interface to inspect or modify graphs, pages, blocks, tags, and properties; run Datascript queries; show page/block trees; manage graphs; and manage db-worker-node servers. Use when a request involves running `logseq` commands or interpreting CLI output.
31logseq-electron-debug
Run Logseq Electron dev build with a remote debugging port and attach Chrome DevTools (CDP) reliably.
6clojure-debug
Debugging workflow for Clojure/ClojureScript code. Use at the first sign of unexpected behavior or test failure in Clojure/CLJS, including any failing test, unexpected output, nils where data is expected, mismatched selectors, or unclear data flow—before making further implementation changes.
4clojure-malli
|
3clojure-paren-repair
Repair unbalanced parentheses, brackets, and braces in Clojure, ClojureScript, and EDN files. Use when you encounter delimiter mismatch syntax errors after editing .clj, .cljs, .cljc, or .edn files, or on clojure syntax errors.
3