How to evaluate, pin, and add Clojars libraries in current Clojure projects without over-relying on older Leiningen-first workflows.
Clojars is the main community repository for Clojure libraries. In practical terms, it is where many of the libraries you actually use in Clojure projects are published and versioned.
The technical challenge is not just “how do I add a dependency?” The better question is “how do I choose, pin, and maintain third-party libraries without turning the project into a fragile pile of transitive assumptions?”
For new examples, the default teaching model should be the Clojure CLI and deps.edn. Older guides often over-center project.clj, but current official Clojure guidance is built around deps.edn, aliases, and the CLI tools.
That does not mean Leiningen is invalid. It means new baseline guidance should reflect current defaults first, then mention Leiningen as inherited context when relevant.
Before adding a dependency, check more than the library name and the first code sample.
Review:
The cheapest dependency is the one you never needed.
The official Clojure CLI guide shows a useful pattern for discovering available versions:
1clj -X:deps find-versions :lib clojure.java-time/clojure.java-time
That helps you avoid copying stale version numbers from random blog posts or old answers.
deps.ednFor a CLI-driven project, adding a library usually means declaring it under :deps:
1{:deps {org.clojure/clojure {:mvn/version "1.12.4"}
2 clojure.java-time/clojure.java-time {:mvn/version "1.4.3"}}}
Then the CLI resolves and downloads the dependency when needed.
That model scales better than scattered local setup notes because the dependency graph is explicit and checked into the repo.
Not every dependency belongs in the base runtime classpath. Development-only tooling, formatting tools, benchmarking helpers, and test libraries often fit better in aliases.
1{:paths ["src"]
2 :deps {org.clojure/clojure {:mvn/version "1.12.4"}}
3 :aliases
4 {:test {:extra-paths ["test"]
5 :extra-deps {org.clojure/test.check {:mvn/version "1.1.1"}}}
6 :dev {:extra-paths ["dev"]}}}
This keeps the main application dependency set narrower and makes intent clearer.
If you are working in an older project, dependency declarations may still live in project.clj:
1(defproject my-project "0.1.0-SNAPSHOT"
2 :dependencies [[org.clojure/clojure "1.12.4"]
3 [clojure.java-time "1.4.3"]])
That is fine in an established codebase. The main mistake is teaching it as the only modern way to manage dependencies.
A healthy policy is:
This matters because dependency drift is often discovered only during an upgrade, an incident, or a CI rebuild on a fresh machine.
Adding one small library can pull in:
That is why dependency choice is also architecture choice.
flowchart TD
A["Need a library"] --> B["Evaluate maintenance and fit"]
B --> C["Check versions and docs"]
C --> D["Add explicit dependency"]
D --> E["Inspect transitive graph and runtime impact"]
E --> F{"Still worth it?"}
F -- Yes --> G["Pin and document"]
F -- No --> H["Choose another library or avoid dependency"]
The important decision point is not after production incidents. It is before the dependency becomes part of the project’s long-term surface area.
project.clj as the default for every new exampledeps.edn and the Clojure CLI as the default model.