Browse Learn Clojure Foundations as a Java Developer

Coordinating State with Ref Transactions

Use refs and STM when several in-process identities must change together, and keep each dosync transaction small, pure, and focused on one invariant.

Refs are Clojure’s coordinated state references. They are useful when several independent identities must change as one logical unit.

For Java engineers, refs are closer to database transaction thinking than lock thinking. You express the state transition inside dosync; Clojure’s software transactional memory (STM) gives the transaction a consistent view and commits the changes together.

1(def checking (ref 100M))
2(def savings  (ref 50M))
3
4(dosync
5  (alter checking - 10M)
6  (alter savings  + 10M))

The practical rule is the same throughout this section: keep transactions pure. Do not put I/O, randomness, email, logging side effects, or Java object mutation inside work Clojure may retry.

In this section

  • Understand Software Transactional Memory
    Understand Clojure software transactional memory as an optimistic in-process transaction model for refs, not as a lock replacement for every kind of state.
  • Write Ref Transactions with dosync
    Use dosync, alter, and ref-set to express coordinated ref updates, and design each transaction around one invariant that must commit atomically.
  • Avoid STM Conflicts and Retry Bugs
    Reduce STM conflicts by keeping dosync blocks short, choosing ref boundaries carefully, and removing side effects that would be unsafe if a transaction retries.
  • Choose Practical Ref Use Cases
    Use refs for practical in-process coordination problems such as inventory reservation, paired indexes, and workflow snapshots, while leaving durability and distributed truth to external systems.
Revised on Saturday, May 23, 2026