Predicates and Assertion Functions in Clojure

Use predicates and assertions to express invariants clearly in Clojure without confusing local checks with full boundary validation.

Predicates answer yes-or-no questions about a value. Assertion functions use those predicates or related checks to state what must be true at a particular point in the program.

This sounds simple, but it is one of the most important habits in good Clojure code. Since so much of the language is data-oriented, correctness often depends on expressing invariants clearly:

  • is this input non-empty?
  • is this order still payable?
  • is this shape safe to process?
  • is this branch logically impossible?

Small, named predicates are often the clearest way to make those rules visible.

Why Predicates Matter

Predicates improve code for two reasons:

  • they compress domain meaning into reusable names
  • they keep branching logic close to the business rule

Compare these:

1(when (and (pos? amount) (not (:blocked? account))))
2  ...)
1(defn chargeable-account? [account amount]
2  (and (pos? amount)
3       (not (:blocked? account))))
4
5(when (chargeable-account? account amount)
6  ...)

The second form is easier to read, easier to reuse, and easier to test.

Assertions Have A Different Job

Assertions are not just reusable predicates with louder syntax. They usually express one of two ideas:

  • a local invariant that must hold here
  • a developer-facing contract that signals misuse quickly
1(defn allocate-budget [amount]
2  (assert (pos? amount) "Budget allocation must be positive")
3  {:allocated amount})

That is appropriate when a negative amount indicates a programming or logic error. It is less appropriate when user input is expected to be messy and needs graceful validation.

Boundary Validation vs Local Assertions

This distinction is crucial.

Use boundary validation when external or user input can reasonably be wrong and the program should respond cleanly.

Use assertions when the code is checking an internal assumption that should already have been established.

Confusing the two leads to brittle systems:

  • assertions become user-facing validation by accident
  • or true invariants get handled too softly

Predicates As Domain Vocabulary

Some of the best predicates in Clojure read like domain language:

  • active-user?
  • retryable-error?
  • final-state?
  • authorized-request?

These names do more than shorten code. They make the program tell the reader what distinction matters.

Common Mistakes

The first mistake is writing overly generic predicates that hide important domain distinctions.

The second mistake is scattering raw boolean expressions everywhere instead of naming important checks.

The third mistake is using assertions as a substitute for real validation at system boundaries.

Design Review Questions

Ask these when reviewing predicate and assertion usage:

  • Does the predicate encode a real domain concept?
  • Would naming this check make the code easier to read elsewhere too?
  • Is this an assertion about an internal invariant, or is it really boundary validation?
  • Will the failure mode make sense to the caller?

Good predicate design makes programs read like they understand their own rules.

Loading quiz…
Revised on Thursday, April 23, 2026