Turn the REPL into a daily engineering workflow: connect the editor, evaluate source-backed forms, capture decisions in tests, and avoid hidden state that teammates cannot reproduce.
REPL-driven development is not “type random code until it works.” A good Clojure workflow uses the REPL for fast feedback while keeping source files, tests, and Git as the durable record.
flowchart LR
A["Edit source"] --> B["Evaluate form"]
B --> C["Inspect value"]
C --> D["Adjust code"]
D --> B
C --> E["Write or update test"]
E --> F["Commit source truth"]
The REPL shortens the loop, but the commit still contains source and tests. That distinction keeps the workflow collaborative.
A productive REPL workflow usually evaluates code from the editor, not by pasting large blocks into a terminal.
| Editor setup | What matters most |
|---|---|
| Calva in VS Code | Jack-in/connect flow, evaluate form, evaluate top-level form, test commands |
| Cursive in IntelliJ IDEA | Correct run configuration, project classpath, evaluate form/file |
| CIDER in Emacs | cider-jack-in, namespace evaluation, test helpers, stack traces |
| Terminal only | Good for learning, but less efficient for source-backed work |
Choose the editor that gives you reliable evaluation and clear feedback. Brand loyalty matters less than the ability to evaluate the right form quickly.
Prefer this loop:
Avoid this loop:
The first loop is fast and reviewable. The second loop is private and fragile.
Keep small domain-shaped examples nearby:
1(def sample-order
2 {:order/id 1001
3 :order/customer {:customer/id 42}
4 :order/lines [{:sku "A1" :qty 2 :price 10}
5 {:sku "B2" :qty 1 :price 25}]})
Then evaluate source-backed functions:
1(orders.core/order-total sample-order)
When the sample reveals a real requirement, move it into a test fixture or test body.
The REPL answers “does this work right now?” A test answers “will this keep working?”
| REPL discovery | Durable follow-up |
|---|---|
| A transform returns the right map | Add a unit test |
| A nil case fails | Add an edge-case test |
| A Java interop call needs a type hint | Add source code and a regression test |
| A dependency API behaves differently than expected | Capture the assumption in a wrapper function and test |
This is the same engineering discipline Java teams already value; Clojure just gives you a faster way to find the right assertion.
Long REPL sessions can drift. Use checkpoints:
| Checkpoint | Why it helps |
|---|---|
| Restart after dependency changes | Confirms the classpath from a clean process |
| Run the test suite regularly | Catches REPL-only state assumptions |
| Evaluate from source buffers | Keeps files and runtime aligned |
| Avoid private session-only helpers | Keeps teammates able to reproduce work |
| Document startup commands | Makes onboarding and CI consistent |
If you would normally add a breakpoint, log line, or temporary controller endpoint in Java, first ask whether you can evaluate the relevant function directly in Clojure.
That question pushes you toward: