Browse Learn Clojure Foundations as a Java Developer

Manage Clojure Dependencies in Java Teams

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.

Two Common Project Shapes

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.

Read A deps.edn File

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.

Read A project.clj File

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.

Resolve Conflicts Deliberately

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.

Keep Builds Reproducible

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.

Dependency Review Checklist

Use this before merging a dependency change:

  1. Does the change affect production, test, dev, or build tooling only?
  2. Is the version pinned intentionally?
  3. Did the dependency tree change beyond the expected libraries?
  4. Are any exclusions documented?
  5. Does CI run with the same alias or profile used locally?
  6. Does the Java side see the same classpath in tests and packaged output?

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.

Key Takeaways

  • Clojure dependency management still uses JVM artifacts, but project tooling differs.
  • Do not change build tools casually during a Java-to-Clojure migration.
  • deps.edn centers on classpath data and aliases; Leiningen centers on project automation.
  • Resolve conflicts with reviewed version alignment or exclusions, not guesswork.
  • CI should make the selected alias, profile, JVM version, and dependency tree visible.

Quiz: Dependency Management

### What should a Java team do before changing from Leiningen to deps.edn? - [x] Treat the build-tool change as a separate migration with its own tests and rollout plan. - [ ] Change it automatically whenever Clojure code is added. - [ ] Delete all existing profiles first. - [ ] Ignore packaged output until production. > **Explanation:** Build-tool migration can change classpaths and packaging, so it should not be bundled casually with behavior migration. ### What is the role of `:aliases` in a deps.edn project? - [x] They name optional classpath or execution variations such as test, dev, or build tasks. - [ ] They replace every namespace name. - [ ] They define Java interfaces. - [ ] They automatically resolve all version conflicts. > **Explanation:** Aliases are named EDN maps selected by Clojure CLI commands to modify classpaths or execution behavior. ### Why should dependency exclusions be reviewed carefully? - [x] They can hide required transitive libraries or change runtime behavior. - [ ] They are only comments. - [ ] They work only in Java, not Clojure. - [ ] They always improve startup time. > **Explanation:** Exclusions alter the resolved dependency graph and can break runtime behavior if used without understanding the conflict. ### What is a good CI check after changing dependencies? - [x] Inspect the selected dependency tree and run tests with the same alias or profile used by the build. - [ ] Run only a formatter. - [ ] Skip tests because dependencies are configuration. - [ ] Compare line counts. > **Explanation:** Dependency changes affect the classpath, so CI should verify the actual resolved graph and execution path.
Revised on Saturday, May 23, 2026