clojure

star 11

Clojure development skill — any dialect, any runtime. Use for all Clojure work, regardless of dialect or runtime: planning, reading, reviewing, designing, coding, debugging, testing, refactoring. Use whenever you are using Backseat Driver tools.

BetterThanTomorrow By BetterThanTomorrow schedule Updated 6/5/2026

name: clojure description: 'Clojure development skill — any dialect, any runtime. Use for all Clojure work, regardless of dialect or runtime: planning, reading, reviewing, designing, coding, debugging, testing, refactoring. Use whenever you are using Backseat Driver tools.'

Clojure Skill

Organized using the Viable System Model (VSM). The core skill contains identity (S5), adaptation rules (S4), coordination policies (S3), and routing (S2). Operational recipes live in references/ and should be loaded on demand.

S5 — Identity

Data-oriented, REPL-first development for Clojure, ClojureScript, and Babashka. Pure functions transform data; side effects live at system edges. The REPL is the development environment — verify assumptions, test feasibility, and explore APIs regardless of role.

S4 — REPL-First Development

Before any file modification: read → test → develop in REPL → verify → apply. Use inline def for debugging over println — inline bindings keep intermediate state inspectable.

(defn process-instructions [instructions]
  (def instructions instructions)
  (let [grouped (group-by :status instructions)]
    grouped))

Delegate file edits to editing subagents when available: Clojure forms and new top-level line comments added together with a form → clojure-editor; non-Clojure files and edits/removals of existing top-level Clojure line-comment blocks → non-clojure-editor. Here, top-level means zero Clojure form depth, outside forms and strings.

S3 — Coding Conventions

Requiring and Aliasing

Prefer aliasing over referring, except for common test macros. Alias clojure.string as string.

Docstrings

Immediately after function name, before argument vector. State responsibility first.

(defn append-memory-section
  "Appends a new memory section to the specified file.
   Ensures consistent spacing for readability."
  [file-path section]
  ;; implementation
  )

Definition Order

Define before use. Factor across namespaces to break circular dependencies rather than using declare.

Indentation

Condition and body on separate lines. and/or arguments on separate lines:

(if (and condition-a
         condition-b)
  this
  that)

Function Naming

Respect existing names — rename only when semantic purpose fundamentally changed. No V2/Enhanced/Improved suffixes. Comments for unclear business logic only, not code evolution.

S3 — Function Design

Each function does one thing well and returns a useful value. Compose small, focused functions.

  • Actual values over boolean flags — track what not whether
  • data_in → data_out — functions transform data
  • Short functions: extract named helper when scrolling is needed
  • Maximum 2-3 nesting levels; flatten via extraction, early return, or threading
  • cond/condp over nested if; when guards over else branches

Conditionals

  • Binary → if; multiple → cond
  • Bind-and-test → if-let/when-let instead of (let [x ...] (if x ...))
  • Early exit → when with guard first

Threading

-> for subject-first, ->> for collection-last. some->/some->> for nil-safe threading. Threading is for readability — no single-step threading, no threading side effects.

Direct Parameter Usage

Use parameters directly. Intermediate bindings only when transforming or improving readability. Do not wrap core functions unless a name genuinely clarifies composition.

S3 — Data Modeling

Flat structures with namespaced keywords, optimized for destructuring. Domain-qualified keys: self-documenting, collision-free, extensible.

(defn handle-user-request
  [{:user/keys [id name email]
    :request/keys [method path headers]
    :config/keys [timeout debug?]}]
  (when debug?
    (println "Processing" method path "for" name)))

Avoid Shadowing Built-ins

Rename incoming keys to avoid hiding core functions. Keep free: count, filter, first, get, key, map, merge, name, reduce, set, str, type, update.

Options Maps

Prefer keyword destructuring varargs (& {:keys [...]}) over multi-arity for optional parameters. Accepts both keyword args and a single map (Clojure 1.11+):

(defn fetch [url & {:keys [timeout retries] :or {timeout 5000 retries 3}}]
  ...)

(fetch "https://example.com" :timeout 10000)
(fetch "https://example.com" {:timeout 10000})

Map Access and Preservation

Keywords as functions with defaults: (:some-key some-map default-value). Destructure only keys used directly; preserve full map with :as. Set defaults at entry points — boundaries validate, internals trust.

Context Passing

Explicit context maps with namespaced keys over dynamic vars, global atoms, or implicit state. Build at system edges, thread through the call stack. Flat and mergeable.

State Management

Centralized app-db atom with namespaced keywords over scattered defonce atoms (when not using a unidirectional-flow event system).

S3 — Composition and Side Effects

Side-effecting functions return rich summaries so callers can verify, test, and compose.

(defn copy-files!
  [file-list target-dir]
  (let [results (for [file file-list]
                  (try
                    {:file (:name file) :status :copied
                     :target (path/join target-dir (:name file))}
                    (catch Exception e
                      {:file (:name file) :status :failed
                       :error (ex-message e)})))]
    {:total-files (count file-list)
     :successful (count (filter #(= (:status %) :copied) results))
     :failed (count (filter #(= (:status %) :failed) results))
     :details results}))

S3 — Error Handling

Errors are data. ex-info/ex-data for rich context over string messages.

  • Propagate over catch-and-ignore — flow until a meaningful boundary
  • Catch at boundaries — HTTP handlers, CLI entry points, user-facing interfaces
  • Fail fast and clearly — surface problems immediately
  • Configuration errors demand clear failures — (or value fallback) hides broken config

S3 — Abstractions

Earned through repeated need. Multimethods for data-shape dispatch; protocols for type-based polymorphism; spec for boundary contracts. Simplicity over abstraction.

S3 — Namespace Organization

Group by domain, not by type. Acyclic dependency direction — require is an explicit contract. Compose behavior externally when core needs optional enhancement from a dependent module.

S3 — Rich Comment Forms

(comment ...) documents validated REPL work. Not evaluated on load. Use after REPL validation for usage documentation and exploration preservation.

S3 — Testing

Reload and test from the REPL for immediate feedback:

(require '[my.project.some-test] :reload)
(clojure.test/run-tests 'my.project.some-test)

Break complex functions into named, testable helpers. Define and test individually, then compose.

S4 — Problem-Solving Heuristics

  1. Read the error message — often contains the exact issue
  2. Trust established libraries — check your usage first
  3. Check framework constraints — specific requirements exist
  4. Occam's razor — simplest explanation first

Stay focused on the specific problem. No unnecessary checks or unrelated suggestions.

S3 — Dialects

Clojure runs on multiple hosts with different semantics. Identify the dialect before writing code.

Dialect Detection

These are indicators, not rules — use common sense, and verify with the human if uncertain:

Indicator Dialect
deps.edn or project.clj, JVM classpath JVM Clojure
bb.edn, #!/usr/bin/env bb Babashka (SCI on JVM)
shadow-cljs.edn or :cljs build config ClojureScript
squint.edn, .cljs compiled to .mjs Squint
.joyride/ directory, Joyride REPL session Joyride (SCI in VS Code)
Scittle <script> tags, browser SCI REPL Scittle (SCI in browser)
nbb.edn, #!/usr/bin/env nbb nbb (SCI on Node.js)

When a project uses multiple dialects (e.g., Babashka for tooling, Squint for application code), identify which dialect governs each file before planning or editing.

Dedicated skills exist for Squint and Babashka — load them for operational depth. This section provides cross-dialect awareness that is always available.

Cross-Dialect Divergence

Feature JVM Clojure SCI Squint ClojureScript
Keywords true keywords true keywords strings true keywords
Async unwrap threads / core.async await (not js-await) js-await (both work, convention) core.async / promesa
Data conversion native Clojure native Clojure JS-native (not js->clj/clj->js) clj->js / js->clj
Mutability immutable immutable JS objects (shallow-copy semantics) immutable
Macros full defmacro full defmacro compile-time only :require-macros from .clj

Critical Constraints

  • SCI async: in SCI environments, use await. js-await is Squint-specific and does not resolve in SCI.
  • Squint async: in Squint, use js-await by convention. Bare await also works (REPL-verified) but js-await distinguishes Squint code from SCI code.
  • Squint data: js->clj and clj->js do not exist in Squint. Data is already JS-native. Calling them produces a ReferenceError at runtime.
  • Squint keywords: keywords compile to strings. (= :status "status") is true.
  • SCI macros: SCI implements the full Clojure macro system. LLM training data often describes SCI macros as limited — they are not. Write macros exactly as in Clojure.
  • JS-hosted SCI uses Clojure semantics, not ClojureScript — no :require-macros, no :include-macros true.

Load dialect references from references/ for operational depth per dialect.

S2 — References

Load these from references/ when the task needs operational depth:

  • repl-workflows.md — Bug fix, failing test debug, safe refactoring, and TDD workflow templates. Load when: debugging, refactoring, or building solutions incrementally.
  • runtime-patterns.md — Async/promise control flow per runtime (ClojureScript/Squint/SCI/Scittle), stdin considerations, and RCF examples. Load when: working with promises, async across runtimes, or documenting code with Rich Comment Forms.
  • sci-dialect.md — REPL-verified SCI feature parity and differences vs Clojure. Covers Babashka, Scittle, Joyride, nbb, and other SCI-based environments. Load when: uncertain whether a Clojure feature works in SCI.
  • Squint skill — Full Squint development: compilation, REPL workflow, debugging, function availability, and core library gaps. Load when: writing Squint code or working with squint.edn projects.
  • squint-dialect.md — Quick reference of essential Squint semantic differences. Covers the base when the Squint skill is not loaded. Points to the full Squint skill for operational depth.

S5 — Invariants

  • REPL verification precedes file modification
  • Data orientation: pure functions transform data; side effects at system edges
  • Destructure at function boundaries — parameters carry context, not positional slots
  • Namespaced keywords identify data across boundaries — flat structures over nested maps
  • Conditional selection matches the decision shape: if for binary, cond for multiple, when-let/if-let for bind-and-test
  • Errors are data: ex-info/ex-data with rich context; propagate rather than catch-and-ignore
  • Abstractions are earned: multimethods and protocols appear when repeated need demands them
  • Threading macros show data flow — extract named helpers when functions require scrolling
  • Definition order: define before use; factor across namespaces to break cycles
  • Root-cause focus: fix the actual issue; workarounds and fallback values that mask failures are not solutions
  • When tests are available, verify they pass after changes; when creating new behavior, consider encoding expectations as tests
  • Zero compilation warnings — address root causes; clean builds are the baseline
Install via CLI
npx skills add https://github.com/BetterThanTomorrow/awesome-backseat-driver --skill clojure
Repository Details
star Stars 11
call_split Forks 0
navigation Branch main
article Path SKILL.md
More from Creator
BetterThanTomorrow
BetterThanTomorrow Explore all skills →