Browse Learn Clojure Foundations as a Java Developer

Comments and Docstrings

Document Clojure code with useful line comments, reader-discard forms, REPL-friendly comment blocks, and docstrings that help Java teams understand functions without importing Javadoc habits blindly.

Clojure documentation is close to the code and close to the REPL. You still explain intent, tradeoffs, and public behavior, but the tools are different from Java’s //, /* ... */, and Javadoc.

The best Clojure comments help a reader understand why a value transformation exists, how a boundary behaves, or what a REPL experiment demonstrates. They should not narrate every small form.

Comment Forms at a Glance

Tool Example Best use Java comparison
Line comment ;; normalize once at the boundary Explaining intent near code //
Reader discard #_(println "debug") Temporarily ignoring the next readable form No exact equivalent
comment form (comment ...) REPL scratch work kept in a namespace Sometimes like a development notebook
Docstring (defn f "..." [x] ...) Public function, macro, namespace, or var documentation Javadoc intent, but runtime-accessible

Use each tool for a distinct purpose. Do not turn source files into large prose essays, and do not leave misleading scratch code in production namespaces.

Line Comments

Clojure uses semicolons for line comments. In normal project code, ;; is the common choice for a comment on its own line:

1(defn taxable-total [order]
2  ;; Tax is calculated after discounts because coupons reduce taxable basis.
3  (+ (:subtotal order)
4     (:tax order)))

Prefer comments that explain business rules, boundary decisions, or non-obvious tradeoffs. Avoid comments that merely repeat the form:

1;; Poor: increments x.
2(inc x)

That comment adds no information.

Reader Discard with #_

The reader-discard form tells the Clojure reader to ignore the next form:

1(+ 1
2   #_(expensive-call)
3   2)
4;; => 3

This is useful for short-lived experimentation because it discards one complete form, not just one line. Remove reader-discarded production code once the experiment is over.

comment Blocks for REPL Work

Many Clojure teams keep a (comment ...) block at the bottom of a namespace for REPL examples:

 1(ns billing.invoice
 2  (:require [clojure.repl :refer [doc]]))
 3
 4(defn invoice-total
 5  "Returns the invoice total in cents after discounts and tax."
 6  [invoice]
 7  (+ (:subtotal-cents invoice)
 8     (:tax-cents invoice)
 9     (- (:discount-cents invoice 0))))
10
11(comment
12  (invoice-total {:subtotal-cents 1000
13                  :tax-cents 130
14                  :discount-cents 100})
15
16  (doc invoice-total))

Important distinction: comment is a macro that ignores its body at evaluation time, but the body still must be readable Clojure forms. It is not a raw text block.

Docstrings

Docstrings are attached to Vars and are available in the REPL:

1(require '[clojure.string :as str])
2
3(defn normalize-email
4  "Returns a trimmed, lower-case email address. Does not validate deliverability."
5  [email]
6  (some-> email
7          str/trim
8          str/lower-case))

Good docstrings answer what a caller needs to know:

Docstring should mention Example question
Purpose What does this function promise?
Input expectation Can nil appear? What shape is the map?
Return value Does it return a value, nil, a lazy seq, or a side effect result?
Boundary behavior Does it validate, normalize, throw, retry, or log?
Important non-goals What does it intentionally not do?

Docstrings are especially valuable on public functions, protocol methods, macros, and namespace-level APIs. Private helper functions can often rely on clear names and local context unless behavior is subtle.

Retrieving Documentation

Use clojure.repl/doc during REPL work:

1(require '[clojure.repl :refer [doc]])
2
3(doc normalize-email)

This is one reason Clojure docstrings should be concise and caller-focused. They are not only generated website text; they are live development help.

Java Javadoc Habits to Keep or Drop

Java habit Keep, adapt, or drop Clojure guidance
Explain public API behavior Keep Use docstrings on public Vars
List every parameter mechanically Adapt Mention argument shape when it is not obvious
Generate large external docs first Adapt Keep REPL-visible docs useful; external docs can build on them
Comment every branch Drop Prefer small forms, good names, and tests
Use comments to compensate for unclear code Drop Improve the code shape first

Example: Comment the Boundary, Not the Obvious

Weak version:

1(defn status-label [status]
2  ;; Check if status equals paid.
3  (if (= status :paid)
4    ;; Return Paid.
5    "Paid"
6    ;; Return Pending.
7    "Pending"))

Better version:

1(defn status-label
2  "Returns the UI label for invoice status. Unknown statuses are shown as pending until the workflow table is migrated."
3  [status]
4  (if (= status :paid)
5    "Paid"
6    "Pending"))

The second version documents a real product decision instead of narrating syntax.

Practice

  1. Rewrite an obvious line comment into a useful “why” comment.
  2. Add a docstring to a function that accepts a map and returns a normalized map.
  3. Use #_ to discard one nested form, then remove it.
  4. Create a small (comment ...) block with one example call and one (doc ...) call.

Key Takeaways

  • Use comments to explain intent, tradeoffs, and boundary behavior, not obvious syntax.
  • Use #_ for temporary form-level discard during experiments.
  • Use (comment ...) blocks for readable REPL scratch forms, not arbitrary text.
  • Write docstrings for public functions and APIs so the REPL helps future readers.
  • Keep the Java Javadoc discipline of documenting contracts, but drop boilerplate parameter narration when names and data shapes are clear.

Quiz: Comments and Docstrings

### What is the common line-comment marker in Clojure project code? - [x] `;;` - [ ] `//` - [ ] `` - [ ] `--` > **Explanation:** Clojure uses semicolons for line comments; `;;` is commonly used for a standalone source comment. ### What does `#_` do? - [x] It discards the next readable form. - [ ] It starts a Java block comment. - [ ] It defines a docstring. - [ ] It imports a namespace. > **Explanation:** Reader discard ignores the next complete form as the source is read. ### What must be true about forms inside a `(comment ...)` block? - [x] They still must be readable Clojure forms. - [ ] They can contain arbitrary unbalanced text. - [ ] They are always executed at startup. - [ ] They must be Java code. > **Explanation:** `comment` ignores its body at evaluation time, but the reader still has to read the forms. ### Where should caller-facing function documentation usually go? - [x] In a docstring attached to the function Var. - [ ] Only in a trailing line comment. - [ ] Only in a Git commit message. - [ ] In a hidden mutable field. > **Explanation:** Docstrings are visible through REPL tooling and travel with the function definition. ### True or False: Good comments should mostly restate what each Clojure form does. - [ ] True - [x] False > **Explanation:** Good comments explain intent, tradeoffs, and contracts; obvious form narration creates noise.
Revised on Saturday, May 23, 2026