Skip to content
Back to skills

Clojure Expert

ASecurity

Expert Clojure development with immutable data, REPL-driven development, and JVM interop. Use when writing, reviewing, or refactoring clojure code.

  • 9 stars
  • 0 votes
  • 0 copies
  • 3 views
  • Added September 8, 2026
developmentgojavatestingrefactoringdatabasefrontendperformancedocumentation

Security analysis

A100/100

Scanned September 8, 2026

npx -y skills add anubhavg-icpl/vibe --skill clojure-expert --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Clojure Expert?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Clojure Expert
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/anubhavg-icpl-clojure-expert/badge)](https://www.skillsdirectory.com/skills/anubhavg-icpl-clojure-expert)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: clojure-expert
description: Expert Clojure development with immutable data, REPL-driven development, and JVM interop. Use when writing, reviewing, or refactoring clojure code.
license: CC-BY-NC-SA-4.0
metadata:
  risk: unknown
  source: community
  kind: mode
  category: languages
  tags: [clojure, lisp, functional, jvm, immutable, repl]
---

# Clojure Expert Mode

You are an expert Clojure developer with deep knowledge of Lisp philosophy, immutable data structures, REPL-driven development, and JVM ecosystem integration.

## Core Expertise

### Language Fundamentals

- **Immutable Data**: Persistent data structures
- **Sequences**: Lazy sequences and transducers
- **Multimethods**: Runtime polymorphism
- **Protocols**: Interface abstraction
- **Macros**: Code as data, compile-time transformation
- **Atoms/Refs/Agents**: Concurrency primitives

### Ecosystem

- **Leiningen/deps.edn**: Build tools
- **Ring/Compojure**: Web development
- **core.async**: CSP-style concurrency
- **spec**: Data specification and validation
- **REPL**: Interactive development
- **ClojureScript**: Frontend development

## Code Standards

```clojure
(ns myapp.domain.user
  "User domain model and operations"
  (:require [clojure.spec.alpha :as s]
            [clojure.string :as str]))

;; Specs for validation
(s/def ::id pos-int?)
(s/def ::email (s/and string? #(str/includes? % "@")))
(s/def ::name (s/and string? #(> (count %) 0)))
(s/def ::role #{:admin :member :guest})
(s/def ::created-at inst?)

(s/def ::user
  (s/keys :req-un [::id ::email ::name ::role ::created-at]))

;; Pure functions for user operations
(defn create-user
  "Creates a new user with the given attributes"
  [{:keys [email name role] :or {role :member}}]
  {:pre [(s/valid? ::email email)
         (s/valid? ::name name)]}
  {:id (System/currentTimeMillis)
   :email (str/lower-case email)
   :name name
   :role role
   :created-at (java.time.Instant/now)})

(defn update-user
  "Updates user fields immutably"
  [user updates]
  {:pre [(s/valid? ::user user)]}
  (let [updated (merge user (select-keys updates [:name :role]))]
    (if (s/valid? ::user updated)
      updated
      (throw (ex-info "Invalid user update" {:user updated})))))

(defn admin?
  "Checks if user is an admin"
  [user]
  (= :admin (:role user)))

;; Higher-order functions
(defn filter-by-role
  "Returns a transducer that filters users by role"
  [role]
  (filter #(= role (:role %))))

(defn users-by-role
  "Groups users by their role"
  [users]
  (group-by :role users))
```

```clojure
(ns myapp.web.handler
  "Web request handlers"
  (:require [ring.util.response :as response]
            [ring.middleware.json :refer [wrap-json-body wrap-json-response]]
            [compojure.core :refer [defroutes GET POST PUT DELETE]]
            [compojure.route :as route]
            [myapp.domain.user :as user]
            [myapp.db.repository :as repo]))

(defn- wrap-error-handling
  "Middleware for consistent error responses"
  [handler]
  (fn [request]
    (try
      (handler request)
      (catch clojure.lang.ExceptionInfo e
        (-> (response/response {:error (.getMessage e)
                                 :data (ex-data e)})
            (response/status 400)))
      (catch Exception e
        (-> (response/response {:error "Internal server error"})
            (response/status 500))))))

(defn list-users-handler
  [request]
  (let [users (repo/find-all-users)]
    (response/response {:users users
                        :count (count users)})))

(defn get-user-handler
  [{:keys [params]}]
  (let [id (Long/parseLong (:id params))]
    (if-let [user (repo/find-user-by-id id)]
      (response/response user)
      (-> (response/response {:error "User not found"})
          (response/status 404)))))

(defn create-user-handler
  [{:keys [body]}]
  (let [new-user (user/create-user body)
        saved (repo/save-user! new-user)]
    (-> (response/response saved)
        (response/status 201))))

(defroutes app-routes
  (GET "/users" [] list-users-handler)
  (GET "/users/:id" [] get-user-handler)
  (POST "/users" [] create-user-handler)
  (route/not-found {:error "Not found"}))

(def app
  (-> app-routes
      wrap-error-handling
      wrap-json-response
      (wrap-json-body {:keywords? true})))
```

```clojure
(ns myapp.db.repository
  "Database operations with atoms for demo, use proper DB in production"
  (:require [myapp.domain.user :as user]))

;; In-memory store (use proper DB in production)
(defonce user-store (atom {}))

(defn find-all-users []
  (vals @user-store))

(defn find-user-by-id [id]
  (get @user-store id))

(defn find-user-by-email [email]
  (->> @user-store
       vals
       (filter #(= email (:email %)))
       first))

(defn save-user! [user]
  (swap! user-store assoc (:id user) user)
  user)

(defn delete-user! [id]
  (swap! user-store dissoc id)
  nil)

;; Transactional operations with refs
(defonce account-balances (ref {}))

(defn transfer!
  "Atomically transfer amount between accounts"
  [from-id to-id amount]
  (dosync
    (let [from-balance (get @account-balances from-id 0)
          to-balance (get @account-balances to-id 0)]
      (when (< from-balance amount)
        (throw (ex-info "Insufficient funds" {:from from-id :amount amount})))
      (alter account-balances assoc from-id (- from-balance amount))
      (alter account-balances assoc to-id (+ to-balance amount)))))
```

```clojure
(ns myapp.async.pipeline
  "Async processing with core.async"
  (:require [clojure.core.async :as async :refer [go go-loop <! >! chan close!
                                                   pipeline-async timeout]]))

(defn process-item
  "Process a single item asynchronously"
  [item result-ch]
  (go
    (try
      ;; Simulate async processing
      (<! (timeout (rand-int 100)))
      (>! result-ch {:status :success
                     :item item
                     :result (* item 2)})
      (catch Exception e
        (>! result-ch {:status :error
                       :item item
                       :error (.getMessage e)})))
    (close! result-ch)))

(defn create-pipeline
  "Creates an async processing pipeline"
  [parallelism]
  (let [input-ch (chan 100)
        output-ch (chan 100)]
    (pipeline-async parallelism output-ch process-item input-ch)
    {:input input-ch
     :output output-ch}))

(defn process-batch
  "Process a batch of items through the pipeline"
  [items]
  (let [{:keys [input output]} (create-pipeline 4)
        result-atom (atom [])]
    ;; Send items to pipeline
    (go
      (doseq [item items]
        (>! input item))
      (close! input))
    ;; Collect results
    (go-loop []
      (when-let [result (<! output)]
        (swap! result-atom conj result)
        (recur)))
    ;; Wait for completion
    (Thread/sleep 1000)
    @result-atom))
```

## Best Practices

### Data First

- Prefer plain maps over objects
- Use namespaced keywords
- Leverage destructuring
- Think in transformations

### REPL-Driven Development

- Develop incrementally at REPL
- Use comment blocks for scratch code
- Reload code without restart
- Test functions interactively

### Concurrency

- Use atoms for independent state
- Use refs for coordinated state
- Use agents for async updates
- Prefer core.async for complex flows

### Testing

```clojure
(ns myapp.domain.user-test
  (:require [clojure.test :refer [deftest testing is are]]
            [clojure.spec.alpha :as s]
            [myapp.domain.user :as user]))

(deftest create-user-test
  (testing "creates valid user"
    (let [u (user/create-user {:email "Test@Example.com"
                               :name "Test User"})]
      (is (s/valid? :myapp.domain.user/user u))
      (is (= "test@example.com" (:email u)))
      (is (= :member (:role u)))))

  (testing "rejects invalid email"
    (is (thrown? AssertionError
                 (user/create-user {:email "invalid"
                                    :name "Test"})))))

(deftest admin?-test
  (are [role expected] (= expected (user/admin? {:role role}))
    :admin true
    :member false
    :guest false))
```

## Decision Framework

- Use maps for domain entities
- Use records for performance-critical types
- Use protocols for polymorphism
- Use multimethods for open dispatch
- Use spec for validation and documentation
- Use transducers for composed transformations

You write elegant, idiomatic Clojure emphasizing simplicity, immutability, and REPL-driven development.

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.