Manage Clojure dependencies with Java-team discipline by understanding deps.edn, Leiningen, classpaths, aliases, exclusions, version alignment, reproducible builds, and CI checks.
Dependency management feels familiar to Java engineers because Clojure runs on the JVM and uses Maven coordinates. The unfamiliar part is the tool shape: many Clojure projects use deps.edn and the Clojure CLI, while many established projects still use Leiningen and project.clj.
The practical rule is simple: respect the tool already chosen by the project, keep dependency changes small, and make the classpath reviewable in CI.
| Project shape | How to think about it |
|---|---|
Clojure CLI / tools.deps with deps.edn |
Lightweight classpath construction, aliases, scripts, libraries, and projects that compose tools explicitly. |
Leiningen with project.clj |
Established applications that want built-in project automation, profiles, plugins, tests, and uberjar tasks. |
Do not migrate build tools as a side effect of migrating Java code. Change the build tool only when the team has a separate reason and a tested rollout plan.
A deps.edn file is Clojure data that describes paths, dependencies, and aliases.
1{:paths ["src" "resources"]
2 :deps {org.clojure/clojure {:mvn/version "1.12.4"}
3 metosin/jsonista {:mvn/version "0.3.13"}}
4 :aliases
5 {:test {:extra-paths ["test"]
6 :extra-deps {io.github.cognitect-labs/test-runner
7 {:git/tag "v0.5.1"
8 :git/sha "dfb30dd"}}}
9 :dev {:extra-paths ["dev"]}}}
For Java developers, the main translation is:
| deps.edn key | Java-team interpretation |
|---|---|
:paths |
Source and resource roots placed on the classpath. |
:deps |
Project dependencies, usually Maven coordinates or Git/local coordinates. |
:aliases |
Named classpath or execution variations for tests, dev, tools, or build tasks. |
:extra-deps |
Dependencies added only when an alias is selected. |
:override-deps |
A deliberate version override used to align a dependency graph. |
The Clojure CLI can inspect the dependency graph with the built-in :deps programs, for example clj -X:deps tree and clj -X:deps list.
Leiningen uses a project.clj form.
1(defproject billing-rules "0.1.0-SNAPSHOT"
2 :description "Billing rule migration slice"
3 :dependencies [[org.clojure/clojure "1.12.4"]
4 [metosin/jsonista "0.3.13"]]
5 :profiles {:dev {:dependencies [[criterium "0.4.6"]]}
6 :uberjar {:aot :all}})
Leiningen is closer to a traditional build tool: project metadata, profiles, plugins, packaging, and tasks live together. That can be comfortable for Java teams, but it also means build behavior may be affected by active profiles.
Dependency conflicts should be handled like production code changes: small, reviewed, and tested.
| Problem | Resolution path |
|---|---|
| Transitive dependency brings the wrong version | In deps.edn, use :override-deps or :exclusions after confirming why. In Leiningen, use :exclusions or align the direct dependency. |
| Test dependency leaks into production | In deps.edn, put it under a test/dev alias. In Leiningen, put it under a dev/test profile. |
| Two libraries require incompatible versions | Prefer upgrading one library or isolating the boundary, regardless of the build tool. |
| Classpath differs between developer machines | Commit project config, document user-level assumptions, and avoid hidden user profile requirements for builds. |
| CI succeeds but local REPL fails | Compare aliases or profiles, plugins, JVM version, and user config. |
Exclusions are not cleanup. They are sharp tools. Every exclusion should have a short reason in the commit or nearby documentation.
Java teams often expect build repeatability. Clojure teams should keep the same standard.
| Check | Why it matters |
|---|---|
| Pin important versions | Avoid surprise behavior changes from implicit upgrades. |
| Keep dev-only tooling out of production paths | Reduce attack surface, startup cost, and classpath confusion. |
| Run dependency tree checks in CI when changing deps | Catch transitive changes before deployment. |
| Document required JVM version | Clojure still runs on the JVM, and Java version drift matters. |
| Avoid hidden global configuration | Builds should not depend on one developer’s user-level files. |
If a migration slice adds Clojure to a Java build, keep the dependency change separate from large behavior rewrites. That makes failures easier to attribute.
Use this before merging a dependency change:
This checklist is especially important when Clojure is introduced inside an existing Java application. A classpath issue can look like a language problem even when the real cause is build configuration.
deps.edn centers on classpath data and aliases; Leiningen centers on project automation.