Understand how deps.edn, clj, clojure, aliases, classpath roots, main invocations, exec functions, and tools aliases fit together in modern Clojure CLI projects.
The Clojure CLI is not a Maven clone. It is a small set of commands that reads deps.edn, builds a classpath, and starts Clojure in a few different modes.
For Java engineers, the most useful translation is:
| Java concept | Clojure CLI concept |
|---|---|
| Build file dependency section | :deps in deps.edn |
| Source set | :paths and alias :extra-paths |
| Run task | :main-opts with clojure -M |
| Tool task | :exec-fn with clojure -X or tools with -T |
| Classpath inspection | clojure -Spath |
| Interactive shell | clj or an editor-connected REPL |
clj vs clojureThe installed CLI commonly gives you two commands:
| Command | Practical use |
|---|---|
clj |
Start an interactive REPL, often with line-editing support |
clojure |
Run scripts, main functions, exec functions, and automation commands |
For learning, use clj when you want a terminal REPL. Use clojure in scripts, README command blocks, and CI examples.
deps.edn KeysA compact project file:
1{:paths ["src" "resources"]
2 :deps {org.clojure/clojure {:mvn/version "1.12.5"}
3 org.clojure/data.json {:mvn/version "2.5.2"}}
4 :aliases
5 {:dev {:extra-paths ["dev"]}
6 :run {:main-opts ["-m" "orders.core"]}
7 :test {:extra-paths ["test"]
8 :main-opts ["-m" "orders.test-runner"]}}}
Read it as classpath data:
| Key | Meaning |
|---|---|
:paths |
Base classpath roots |
:deps |
Libraries and versions |
:aliases |
Named modifications to classpath, JVM options, or command behavior |
:extra-paths |
Add paths only when an alias is used |
:extra-deps |
Add dependencies only when an alias is used |
:main-opts |
Options passed when running with -M |
Unlike Maven or Gradle, deps.edn does not define a full lifecycle. It describes how to assemble code and dependencies for the command you choose.
The CLI flags matter because they activate aliases differently.
| Mode | Example | Use it for |
|---|---|---|
| REPL | clj |
Interactive exploration |
| Classpath inspection | clojure -Spath |
Diagnosing dependency/path problems |
| Main invocation | clojure -M:run |
Running a namespace with -main |
| Exec function | clojure -X:report |
Calling a named function with data arguments |
| Tool invocation | clojure -T:build uber |
Running build/tool functions outside the app classpath |
For a first project, -M is enough. Learn -X and -T when you start using task functions and build tools.
-MAlias:
1{:aliases
2 {:run {:main-opts ["-m" "orders.core"]}}}
Namespace:
1(ns orders.core)
2
3(defn -main
4 [& args]
5 (println "Args:" args))
Run:
1clojure -M:run 1001
This is closest to a Java main method: the namespace loads, then -main receives command-line arguments.
-XExec functions are useful for data-oriented tasks:
1(ns orders.tasks)
2
3(defn report
4 [{:keys [format limit]}]
5 (println "format =" format "limit =" limit))
Alias:
1{:aliases
2 {:report {:exec-fn orders.tasks/report
3 :exec-args {:format "edn" :limit 10}}}}
Run:
1clojure -X:report :limit 5
Think of -X as “call this task function with an EDN map,” not as a shell-style main argument parser.
-TTools aliases are commonly used for build functions, especially with tools.build:
1{:aliases
2 {:build {:deps {io.github.clojure/tools.build {:mvn/version "0.10.13"}}
3 :ns-default build}}}
Run:
1clojure -T:build uber
The -T mode is for tool execution. Keep it conceptually separate from your application’s normal runtime classpath.
| Mistake | Symptom | Fix |
|---|---|---|
Using -A for a :main-opts command |
Main function does not run as expected | Use -M:alias for main invocation |
Putting tests in :paths permanently |
Test dependencies leak into normal runs | Use an alias with :extra-paths |
Treating deps.edn as a lifecycle build file |
Confusion about packaging or deploy tasks | Add explicit tool aliases or build scripts |
| Not inspecting the classpath | Namespace errors feel mysterious | Run clojure -Spath |
Mixing clj and clojure randomly in scripts |
Automation differs from local REPL behavior | Prefer clojure in scripts and CI |
deps.edn is classpath and command data, not a full build lifecycle.clj is convenient for interactive terminal REPL work; clojure is better for scripts and automation.-M for -main, -X for exec functions, and -T for tools.