Browse Learn Clojure Foundations as a Java Developer

Clojure Style and Formatting

Write readable Clojure with idiomatic indentation, kebab-case names, predicate and mutation markers, namespace-aware formatting, and formatter habits that reduce code review noise.

Clojure style is not about making Lisp look like Java. It is about making nested forms, immutable values, and namespace-qualified calls easy to scan.

For Java engineers, the biggest adjustment is that formatting carries structure. Braces and type declarations are gone, so indentation, naming, and form shape do more work.

What Good Clojure Style Optimizes For

Goal Java habit to reconsider Clojure habit to build
Scan form structure Align around braces and semicolons Indent forms by semantic shape
Read names quickly Use camelCase Use kebab-case symbols
See predicates Prefix with is or has End names with ?
Mark side effects Hide mutation inside methods Use names and boundaries that expose effects
Reduce review noise Debate formatting manually Let a formatter handle whitespace

Style is part of correctness because poorly formatted Clojure hides evaluation order.

Indentation: Follow The Form

Function bodies usually indent two spaces:

1(defn invoice-total [invoice]
2  (+ (:subtotal invoice)
3     (:tax invoice)
4     (- (:discount invoice 0))))

Binding forms align name/value pairs:

1(let [subtotal (:subtotal invoice)
2      tax      (:tax invoice)
3      discount (:discount invoice 0)]
4  (- (+ subtotal tax) discount))

Threaded pipelines indent each step:

1(-> invoice
2    normalize-lines
3    apply-discounts
4    calculate-tax)

Do not fight the formatter. If indentation looks strange, first ask whether the form shape is too complex.

Naming Conventions

Clojure names are usually lowercase and hyphenated:

1(defn normalize-email [email]
2  ...)

Important naming markers:

Marker Meaning Example
? Predicate returning truthy/falsey value active-user?
! Side-effecting or state-changing operation save-user!
-> Conversion order->invoice
*earmuffs* Dynamic Var *print-length*
Leading _ Intentionally unused binding _request

These markers are compact contracts. They help reviewers notice when code is pure, effectful, conditional, or conversion-oriented.

Avoid Java Naming Drift

Java-style name Idiomatic Clojure
getUserById user-by-id
isActive active?
calculateInvoiceTotal invoice-total
saveUser save-user! when it performs an effect
orderToInvoice order->invoice

Use verbs when the operation matters, but do not force every function into a Java method-name pattern. In Clojure, many functions are best named after the value they return.

Whitespace in Collections

Readable data literals are as important as readable function calls:

1{:user/id     42
2 :user/email  "dev@example.com"
3 :user/roles  #{:admin :billing}
4 :user/status :active}

Use alignment when it helps compare related keys. Do not over-align unrelated data until small changes create noisy diffs.

Commas are whitespace in Clojure:

1[:a, :b, :c]
2;; same as
3[:a :b :c]

Most idiomatic code omits commas except when they materially improve readability in dense literals.

Comments and Blank Lines

Use blank lines to separate ideas, not every expression:

 1(defn billable-lines [invoice]
 2  (->> (:lines invoice)
 3       (filter billable?)
 4       (map apply-discount)))
 5
 6(defn invoice-total [invoice]
 7  (->> invoice
 8       billable-lines
 9       (map :line/total-cents)
10       (reduce + 0)))

Keep comments for intent and tradeoffs. If a comment only explains parentheses, improve the code shape instead.

Formatter Workflow

Use a formatter such as cljfmt through the build tool your project already uses. The exact command depends on the project, but the review rule is stable:

Workflow point Action
Before committing Run the formatter or formatting check
During review Discuss semantics, not whitespace
When formatter output is ugly Simplify the form or add a formatter hint only if the team accepts it
In mixed Java/Clojure repos Let each language’s formatter own its files

Avoid pinning old dependency snippets in learning material unless you are showing a real project file. Tool versions change; the habit matters more here than a stale version number.

Refactoring For Readability

Bad formatting often signals too much work in one form:

1(defn total-active-cents [users]
2  (reduce + (map :balance-cents (filter (fn [user] (= :active (:status user))) users))))

Prefer a pipeline with named steps when that helps the reader:

1(defn total-active-cents [users]
2  (->> users
3       (filter active-user?)
4       (map :balance-cents)
5       (reduce + 0)))

This is not merely prettier. It makes the data flow reviewable.

Practice

  1. Rename getUserById, isPaid, and saveInvoice into idiomatic Clojure names.
  2. Reformat a dense let so bindings are easy to scan.
  3. Rewrite a nested map/filter/reduce expression into a ->> pipeline.
  4. Decide whether a function name should end with ? or !.

Key Takeaways

  • Clojure formatting should reveal form structure and data flow.
  • Use kebab-case names, ? for predicates, ! for effectful operations, and -> for conversions.
  • Prefer small, readable forms over clever one-liners.
  • Let formatters reduce whitespace debates, but still refactor when formatting exposes complexity.
  • Do not import Java camelCase and method-naming habits into idiomatic Clojure.

Quiz: Clojure Style and Formatting

### What naming style is most common for Clojure functions? - [x] Lowercase kebab-case. - [ ] Java camelCase. - [ ] Uppercase snake case. - [ ] Hungarian notation. > **Explanation:** Clojure names usually use lowercase words separated by hyphens. ### What does a trailing `?` usually indicate? - [x] A predicate. - [ ] A Java import. - [ ] A namespace alias. - [ ] A formatter warning. > **Explanation:** Predicate names commonly end with `?`, such as `active?` or `paid?`. ### What does a trailing `!` often signal? - [x] A side effect or state-changing operation. - [ ] Guaranteed purity. - [ ] A map literal. - [ ] A reader comment. > **Explanation:** A `!` warns readers that a function performs an effect or state change. ### Why use formatter tooling? - [x] To reduce whitespace noise and keep review focused on semantics. - [ ] To make code mutable. - [ ] To replace tests. - [ ] To generate Java classes. > **Explanation:** Formatting tools make layout consistent so reviews can focus on behavior. ### True or False: If Clojure indentation looks painful, the form may need to be simplified. - [x] True - [ ] False > **Explanation:** Awkward indentation often reveals an over-nested or overloaded form.
Revised on Saturday, May 23, 2026