Coding Style and Conventions in Clojure

Learn the Clojure style choices that most affect readability, team consistency, and tool compatibility, including naming, formatting, namespace shape, and pipeline clarity.

Style matters more in Clojure than many people first expect. The language is compact, heavily nested, and structurally regular. That means small naming or formatting choices have an outsized effect on readability.

The goal of style is not decoration. It is to make code easier to scan, easier to review, and less ambiguous under change.

Prefer the Community’s Defaults

In Clojure, consistency is usually more valuable than personal cleverness. If a team already follows common community conventions, keep using them unless there is a clear reason to diverge.

That generally means:

  • kebab-case for most names
  • small, focused namespaces
  • threading macros for readable pipelines
  • data-oriented APIs over object-style naming
  • automatic formatting rather than hand-tuned personal indentation habits

Naming Conventions

Functions and Locals

Use lowercase words separated by hyphens:

1(defn calculate-total [line-items]
2  ...)

Avoid Java-style camelCase unless the name comes directly from interop boundaries.

Predicates

Predicates should normally end in ?.

1(defn active-user? [user]
2  (:active? user))

This is one of the most helpful readability conventions in the language because it tells the reader what kind of value to expect.

Side-Effecting Functions

Names ending in ! conventionally signal an operation that may have an important effect outside pure value calculation.

1(reset-cache!)
2(save-order!)

The exclamation mark does not mean “mutates memory in place” in some strict technical sense. It is a warning that the function is effectful or operationally significant.

Constants

In idiomatic Clojure, it is often clearer to use ordinary var names for stable values than to force an all-caps constant style everywhere. Reserve loud constant naming for values that genuinely act like global constants and would otherwise be missed.

Namespace Conventions

Namespace names should describe role and domain clearly.

1my-app.orders.core
2my-app.orders.validation
3my-app.http.handlers

Good namespace boundaries usually separate:

  • domain logic
  • I/O boundaries
  • orchestration
  • transformation utilities

The goal is not maximum granularity. The goal is to keep related code together without turning namespaces into unstructured dumping grounds.

Formatting Choices That Matter

Let the Formatter Win

In modern Clojure work, code format should usually be enforced automatically. Debating personal spacing style by hand is wasted review energy.

Use a formatter such as cljfmt, and treat manual formatting preferences as secondary to consistent tool-driven output.

Indentation Is Structural, Not Decorative

Indentation should reveal shape:

1(defn process-order [order]
2  (let [validated (validate-order order)
3        priced    (price-order validated)]
4    (persist-order! priced)))

The point is not merely alignment. The point is that the reader can quickly see:

  • outer function
  • local bindings
  • final expression

Prefer Readable Pipelines

When several transformations happen in order, threading macros usually improve readability.

1(->> users
2     (filter :active?)
3     (map :email)
4     (remove nil?)
5     (into []))

Do not force threading macros into every expression, but use them when they clarify data flow.

Destructuring for Clarity

Destructuring often makes function signatures and local extraction cleaner:

1(defn display-name [{:keys [first-name last-name]}]
2  (str first-name " " last-name))

This is usually clearer than repeated nested key lookups inside the function body.

The mistake is overdestructuring. If a binding form starts to look like a puzzle, the function may be trying to absorb too much structure at once.

Avoiding Common Style Drift

Deeply Nested Anonymous Code

Several layers of inline fn or #() often become harder to read than a small named helper.

Java-Like Verbosity

Clojure code often gets worse when it imitates Java naming and class-style ceremony instead of using the language’s smaller, data-oriented forms naturally.

One Namespace That Does Everything

A namespace named core is normal. A namespace that contains parsing, HTTP, persistence, validation, metrics, and business rules all together is usually not.

Clever Formatting That Fights the Toolchain

If the formatter will change it anyway, do not build team habits around custom spacing tricks.

Style and Review Quality

The real payoff of conventions appears in code review:

  • readers can focus on meaning instead of layout noise
  • diffs are easier to scan
  • rename and extraction changes are safer
  • the codebase feels like one system rather than many personal dialects

That is why style should be treated as a maintainability tool, not as an individual signature.

Design Review Question

A codebase mixes camelCase, kebab-case, manually aligned indentation, formatter-generated indentation, and several personal preferences about when to use threading macros. None of the choices is individually catastrophic, but reviews are noisy and the code feels inconsistent.

What is the stronger fix?

The stronger fix is to adopt a small explicit style baseline, enforce formatting automatically, and make naming and pipeline conventions consistent enough that review effort shifts away from layout debates toward design and correctness.

Key Takeaways

  • Clojure style should optimize readability and consistency, not personal flair
  • kebab-case, predicate ?, and effectful ! naming conventions carry real semantic value
  • formatter-driven consistency is usually better than manual style negotiation
  • namespace boundaries should reflect roles and domains clearly
  • threading and destructuring help when they clarify data flow, not when used performatively
Loading quiz…
Revised on Thursday, April 23, 2026