100% Free Forever
AI-Powered Learning
Industry Expert Content
Certificates & Badges
Learn At Your Own Pace
Programming

Leiningen and deps.edn

Compare Clojure's two main build tools — Leiningen's project.clj and the official tools.deps CLI's deps.edn — and learn how each manages dependencies, profiles, and aliases.

Practical ClojureBeginner9 min readJul 10, 2026
Analogies

Two Build Tooling Paths

Clojure has two mainstream tools for managing dependencies, the classpath, and common project tasks: Leiningen, the older and still widely used batteries-included tool configured via project.clj, and the official tools.deps CLI configured via deps.edn. Both resolve library versions from Maven-style coordinates and build a classpath, but they differ sharply in philosophy — one bundles conventions and a large plugin ecosystem, the other stays deliberately minimal and composable.

🏏

Cricket analogy: Choosing between Leiningen and the deps.edn tools.deps CLI is like a team choosing between a traditional, everything-included tour manager versus building your own support staff a la carte, composing exactly the pieces you need, nothing more.

Leiningen: project.clj Anatomy

A Leiningen project centers on defproject, which declares the project's name, version, and dependencies in one map-like form. :dependencies lists required libraries, :plugins lists build-time tooling like lein-ring, and :profiles layers extra configuration onto specific contexts — most commonly :dev for development-only dependencies and :uberjar for producing a standalone runnable JAR via lein uberjar.

🏏

Cricket analogy: A defproject form is like a team's official registration document naming the squad, the support staff hired for the tour, and separate training regimes for practice matches versus the actual final, all filed before lein run kicks off the innings.

deps.edn and tools.deps

deps.edn declares :paths (source and resource directories on the classpath), :deps (a map of library coordinates to versions), and :aliases, which are named, opt-in configurations for extra paths, dependencies, or main options. Nothing in an alias applies unless invoked explicitly — clj -M:alias runs a namespace's -main function, while clj -X:alias invokes a specific function directly, passing trailing key/value pairs as an EDN argument map.

🏏

Cricket analogy: deps.edn's :paths is like specifying exactly which nets and pitches the team practices on, :deps lists the specific sparring partners brought in, and :aliases are named custom drills, invoked only when called by name on the command line.

clojure
;; project.clj (Leiningen)
(defproject myapp "0.1.0-SNAPSHOT"
  :description "Example service"
  :dependencies [[org.clojure/clojure "1.11.1"]
                 [ring/ring-core "1.12.0"]
                 [cheshire "5.12.0" :exclusions [org.clojure/clojure]]]
  :plugins [[lein-ring "0.12.6"]]
  :profiles {:dev  {:dependencies [[ring/ring-devel "1.12.0"]]}
             :uberjar {:aot :all}}
  :main ^:skip-aot myapp.core)

;; deps.edn (tools.deps)
{:paths ["src" "resources"]
 :deps  {org.clojure/clojure {:mvn/version "1.11.1"}
         ring/ring-core      {:mvn/version "1.12.0"}}
 :aliases
 {:dev  {:extra-paths ["dev"]
         :extra-deps  {ring/ring-devel {:mvn/version "1.12.0"}
                       nrepl/nrepl     {:mvn/version "1.1.0"}}}
  :test {:extra-paths ["test"]
         :extra-deps  {io.github.cognitect-labs/test-runner
                       {:git/tag "v0.5.1" :git/sha "dfb30dd"}}
         :main-opts   ["-m" "cognitect.test-runner"]}}}

Rule of thumb: reach for Leiningen when you want batteries-included conventions (uberjar packaging, a huge plugin ecosystem, one config format for everything). Reach for deps.edn/tools.deps when you want a lighter, official, more composable setup — especially for libraries, or when you want fine-grained control via aliases without a plugin layer in between.

Managing Dependencies and Profiles/Aliases

Both tools resolve a full transitive dependency graph and must settle conflicts when two libraries pull in different versions of the same underlying artifact. :exclusions lets you explicitly drop an unwanted transitive dependency so a chosen version wins deterministically. lein deps :tree and clj -Stree both print the resolved graph, which is the first thing to check when a classpath issue or unexpected class shows up at runtime.

🏏

Cricket analogy: Two selectors independently recommending the same all-rounder for different reasons is fine, but two different scouting reports disagreeing on the same player's exact fitness rating forces the selection committee to pick one authoritative source, just as :exclusions removes a duplicate transitive dependency pulling in a conflicting version.

Transitive dependency conflicts are one of the most common build headaches in both tools. Run lein deps :tree (Leiningen) or clj -Stree (tools.deps) to see the resolved dependency graph, and use :exclusions to drop an unwanted transitive version before it silently shadows the one you actually need.

  • Leiningen (project.clj) and tools.deps (deps.edn) are the two mainstream Clojure build tools; both manage dependencies and the classpath.
  • defproject in project.clj declares :dependencies, :plugins, and environment-specific :profiles like :dev and :uberjar.
  • deps.edn declares :paths (source directories), :deps (libraries), and :aliases (named, explicitly-invoked task configurations).
  • clj -M:alias runs a main namespace under an alias's extra deps/paths; clj -X:alias invokes a specific function with an EDN argument map.
  • Leiningen is batteries-included with a large plugin ecosystem; tools.deps is the leaner, official, more composable option favored for libraries.
  • Use :exclusions to drop an unwanted transitive dependency version when two libraries pull in conflicting versions of the same artifact.
  • lein deps :tree and clj -Stree both print the resolved dependency graph to help debug version conflicts.

Practice what you learned

Was this page helpful?

Topics covered

#Programming#ClojureStudyNotes#LeiningenAndDepsEdn#Leiningen#Deps#Edn#Two#StudyNotes#SkillVeris#ExamPrep