Browse Learn Clojure Foundations as a Java Developer

Understand deps.edn and the Clojure CLI

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 clojure

The 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.

The Core deps.edn Keys

A 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.

Command Modes

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.

Main Invocation With -M

Alias:

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.

Exec Functions With -X

Exec 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.

Tools With -T

Tools 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.

Common Mistakes

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

Key Takeaways

  • 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.
  • Use -M for -main, -X for exec functions, and -T for tools.
  • Aliases should make classpath differences explicit.
  • Java build-tool instincts help, but the CLI is intentionally smaller and more composable.

Quiz: deps.edn and CLI

### What does `:paths` define in `deps.edn`? - [x] Classpath roots such as `src` and `resources` - [ ] Git branch names - [ ] Test assertion counts - [ ] JVM garbage collector settings only > **Explanation:** `:paths` lists directories that become classpath roots. ### Which mode should you use to run an alias with `:main-opts`? - [x] `clojure -M:alias` - [ ] `clojure -T:alias` - [ ] `lein run :alias` - [ ] `java -version :alias` > **Explanation:** `-M` is the main invocation mode for aliases with `:main-opts`. ### What is `-T` commonly used for? - [x] Running tool functions such as build tasks - [ ] Starting an interactive terminal REPL - [ ] Downloading Java itself - [ ] Replacing `deps.edn` > **Explanation:** `-T` is intended for tool execution, such as functions in a build namespace.
Revised on Saturday, May 23, 2026