Clojure REPL-Driven Development
Core Workflow
Never write code without REPL validation.
Every coding task follows this loop:
- Gather context
- Take action
- Verify output
Before modifying any file:
- Read existing code - Use
readto examine target file and related files - Verify nREPL connection - Test:
clj-nrepl-eval -p 7889 "(+ 1 1)" - If connection fails, discover ports -
clj-nrepl-eval --discover-ports - Initialize dev environment if available -
clj-nrepl-eval -p 7889 "(fast-dev)" - Explore unfamiliar functions -
clj-nrepl-eval -p 7889 "(clojure.repl/doc function-name)" - Test in REPL - Define and validate functions before saving
- Check edge cases - nil, empty collections, invalid inputs
- Save only after validation - Use
editorwrite - Reload before verifying edits -
clj-nrepl-eval -p 7889 "(require '[project.core] :reload)" - Do not report success before verification - changed functions and relevant tests must pass
If nREPL fails, first run clj-nrepl-eval --discover-ports to find existing servers. Only ask the user to start a server if none are found.
If fast-dev is unavailable, continue without it.
Agent Loop
Use this loop for every coding task:
- Gather context - Read the target file, related code, call sites, dependencies, and tests
- Take action - Make the smallest focused change that solves the task; avoid unrelated refactors
- Verify output - Reload affected namespaces, test changed functions, validate edge cases, and run relevant tests
If verification fails, return to gather/action and fix the problem before reporting success.
Failure Recovery
If REPL evaluation, test execution, or namespace loading fails:
- Read the exact error message
- Isolate the failing expression or function
- Fix the root cause
- Reload affected namespaces
- Rerun verification
If delimiter errors occur, use clj-paren-repair instead of manual repair.
Task Communication
For multi-step tasks, briefly communicate:
- what you are reading
- what you are changing
- how you will verify it
Ask for clarification when requirements are ambiguous, multiple approaches have materially different trade-offs, or an architectural decision is required.
Essential Patterns
Threading Macros (always prefer over nesting)
;; -> for transformations
(-> user
(assoc :last-login (Instant/now))
(update :login-count inc))
;; ->> for sequences
(->> users
(filter active?)
(map :email)
(str/join ", "))
;; some-> for nil-safe navigation
(some-> user :address :postal-code (subs 0 5))
;; cond-> for conditional changes
(cond-> request
authenticated? (assoc :user current-user))
Naming Rules
| Pattern | Example |
|---------|---------|
| kebab-case | calculate-total, max-retries |
| predicates end with ? | valid?, active? |
| conversions use -> | map->vector, string->int |
| NEVER use ! suffix | Bad: save-user! Good: save-user |
Control Flow
;; when for side effects
(when (valid? data)
(log "Processing")
(process data))
;; cond for multiple branches
(cond
(< n 0) :negative
(= n 0) :zero
:else :positive)
Docstrings (required for public functions)
(defn calculate-total
"Calculate total price including tax.
Args:
price - base price as BigDecimal
rate - tax rate as decimal (0.08 = 8%)
Returns:
BigDecimal total price
Example:
(calculate-total 100.00M 0.08) => 108.00M"
[price rate]
...)
Namespace Template
(ns project.module
(:require
[clojure.string :as str]
[clojure.set :as set])
(:import
(java.time LocalDate)))
(set! *warn-on-reflection* true)
Tools
clj-nrepl-eval
# Test expressions
clj-nrepl-eval -p 7889 "(+ 1 2 3)"
# Define and test functions
clj-nrepl-eval -p 7889 "(defn sum [nums] (reduce + nums))"
clj-nrepl-eval -p 7889 "(sum [1 2 3])"
# Discover functions
clj-nrepl-eval -p 7889 "(clojure.repl/dir clojure.string)"
clj-nrepl-eval -p 7889 "(clojure.repl/doc map)"
clj-nrepl-eval -p 7889 "(clojure.repl/apropos \"split\")"
clj-nrepl-eval -p 7889 "(clojure.repl/source filter)"
# Load project code
clj-nrepl-eval -p 7889 "(require '[project.core :as core] :reload)"
clj-paren-repair
# Fix delimiter errors
clj-paren-repair src/core.clj
clj-paren-repair src/*.clj test/*.clj
Never manually fix parenthesis errors—use this tool.
Validation Checklist
Before saving any code:
- [ ] Tested happy path in REPL
- [ ] Tested nil handling
- [ ] Tested empty collection handling
- [ ] Used threading macros over deep nesting
- [ ] Added docstring if public function
- [ ] Checked naming conventions (no
!suffix) - [ ] Code under 80 characters per line
- [ ] Closing parens on single line
Code Review Workflow
Before modifying code:
readthe target filebash: rg "require.*target.ns" --type clj- find related filesbash: rg "function-name" --type clj- find call sites- Review namespace imports and patterns
- Match codebase conventions
Detailed References
- Tool usage: See references/tool-guide.md for complete clj-nrepl-eval and clj-paren-repair documentation
- Idiomatic patterns: See references/idioms.md for threading macros, control flow, data structures, error handling, and anti-patterns
Load these references when you need:
- Detailed tool commands
- Advanced idioms or patterns
- Error handling examples
- Testing patterns
- Research citations
Scan to join WeChat group