Browse Learn Clojure Foundations as a Java Developer

Choose Practical Agent Use Cases

Use agents for practical asynchronous state ownership such as telemetry aggregation or ordered local side-effect coordination, while choosing queues, executors, or databases for broader task systems.

Agents are most useful when you can name the state value they own. If the main problem is “run tasks somewhere,” an agent may be the wrong abstraction.

For Java engineers, the distinction is important. An executor owns threads. An agent owns state and happens to process state transitions asynchronously.

Agent Fit Table

Scenario Fit Reason
Aggregate local telemetry Good One state value receives serialized updates.
Maintain ordered in-memory audit events Good Order matters for one local collection.
Coordinate after an STM commit Good Agent sends inside STM are held until commit.
Process a durable job queue Poor Needs persistence, retries, and backpressure.
Return immediate API response data Poor Caller needs a result, not eventual state.
Coordinate several independent identities Poor Refs or another transaction boundary fit better.

Use an agent when asynchronous state ownership is the central idea.

Example: Telemetry Aggregation

 1(def telemetry
 2  (agent {:requests 0
 3          :errors 0}))
 4
 5(defn record [m ok?]
 6  (-> m
 7      (update :requests inc)
 8      (update :errors
 9              (fnil + 0)
10              (if ok? 0 1))))
11
12(defn record-request! [ok?]
13  (send telemetry record ok?))

The request thread does not wait for the metrics state to update. That is acceptable because telemetry is useful as an eventually updated local snapshot.

When to Choose Something Else

Need Prefer
Durable work after restart Message broker, database queue, or job system.
Backpressure and stream processing core.async channel, queue, or streaming system.
Immediate computed result Future, promise, direct call, atom, or ref transaction.
Complex supervision Dedicated worker framework or service architecture.
Multi-node shared state External coordinated storage.

Agents can be part of a production design, but they should not be the hidden foundation for durable background processing.

Production Checklist

  • The agent has one clear state value.
  • The owner namespace or component is clear.
  • Actions are small and either CPU-bound with send or blocking-aware with send-off.
  • Errors are observed with handler, metrics, or explicit checks.
  • Shutdown behavior is defined for command-line or batch programs.
  • Durable truth lives outside the agent.

Knowledge Check

### Which use case is a strong agent fit? - [x] Aggregating local telemetry in one state value. - [ ] Persisting a durable job queue. - [ ] Coordinating a database transaction. - [ ] Returning an immediate API result. > **Explanation:** Telemetry aggregation can tolerate asynchronous local state updates. The other cases need durability, transactions, or immediate results. ### What is the key distinction between an executor and an agent? - [x] An executor owns task execution; an agent owns state and processes transitions. - [ ] An agent always creates one Java thread per action. - [ ] An executor stores immutable state by default. - [ ] There is no meaningful distinction. > **Explanation:** Agents are state references with asynchronous actions, not just thread-pool wrappers. ### What should durable background processing use instead of an agent? - [x] A message broker, database queue, or job system. - [ ] A public dynamic Var. - [ ] A local atom with no persistence. - [ ] `@agent` reads in a loop. > **Explanation:** Durable background processing needs restart safety, retries, and often backpressure. Agents are in-process state tools.
Revised on Saturday, May 23, 2026