Browse Learn Clojure Foundations as a Java Developer

Understand Leiningen Projects

Learn how Leiningen uses project.clj, named tasks, profiles, plugins, the project REPL, and uberjar packaging so you can work confidently in existing Clojure repositories.

Leiningen is the Clojure tool Java engineers often understand fastest because it behaves like a project automation tool: one project file, named commands, dependencies, tests, a REPL, and a packaging task.

The important point is not that Leiningen is “the Maven of Clojure.” The better mental model is:

Java habit Leiningen equivalent
Read pom.xml or build.gradle Read project.clj
Run a named build task Run lein test, lein run, or lein uberjar
Start a project-aware shell Run lein repl
Package an executable artifact Run lein uberjar
Use build profiles Use Leiningen profiles

What Leiningen Owns

Leiningen is a project automation tool. In a typical app, it owns:

Concern Leiningen mechanism
Project identity defproject in project.clj
Dependencies :dependencies
Source and test paths Defaults plus optional configuration
REPL startup lein repl
Test execution lein test
Application execution lein run
Packaging lein uberjar
Extensions Plugins and profiles

That makes it a good bridge for Java developers joining mature Clojure projects. If a repository has project.clj, begin by learning its lein workflow instead of immediately proposing a tool migration.

Read project.clj

A small Leiningen project file might look like this:

1(defproject orders-service "0.1.0-SNAPSHOT"
2  :description "Order ingestion service"
3  :dependencies [[org.clojure/clojure "1.12.5"]
4                 [cheshire "5.13.0"]]
5  :main ^:skip-aot orders-service.core
6  :target-path "target/%s"
7  :profiles {:dev {:dependencies [[nrepl "1.1.0"]]}
8             :uberjar {:aot :all}})

Read the file from the outside in:

Form or key Meaning
defproject Project name and version
:dependencies Runtime libraries resolved from Maven-style coordinates
:main Namespace containing -main for lein run and packaging
^:skip-aot Avoids ahead-of-time compilation during normal development
:profiles Environment-specific additions or packaging behavior
:target-path Where generated build output goes

You do not need every Leiningen option on day one. You need to know which command your team expects and which part of project.clj controls that command.

Learn the First Commands

Command Use it when
lein help You need to inspect available tasks
lein new app my-app You want a small application scaffold
lein repl You want a REPL with project dependencies loaded
lein test You want the normal test suite
lein run You want to invoke the configured -main
lein uberjar You want a standalone deployment jar

For everyday development, lein repl is more important than it first appears. It is not just a console; it is an interactive process with the project classpath, dependencies, and namespaces available.

Profiles and Plugins

Leiningen has two extension points you will see in real repositories:

Extension point What it does Java comparison
Profile Adds or overrides config for a context Maven profile or Gradle source set/configuration
Plugin Adds tasks or behavior to Leiningen Maven plugin or Gradle plugin

Profiles are often used for :dev, :test, and :uberjar. Plugins are common in older projects for formatting, linting, test runners, deployment, or framework-specific tasks.

Do not blindly copy profiles between projects. A profile changes the classpath and sometimes the runtime behavior.

When Leiningen Is the Right Tool

Use Leiningen directly when:

  • the repository already has project.clj
  • the README documents lein commands
  • a project depends on Leiningen templates or plugins
  • the team packages with lein uberjar
  • onboarding speed matters more than introducing a new build model

Avoid a tool migration until you can state exactly what problem the migration solves. “The official CLI exists” is not enough by itself.

Common Mistakes

Mistake Result Better move
Treating lein run as the only workflow Slow feedback loop Use lein repl for interactive development
Editing project.clj like a static dependency list only Broken profiles or packaging Read task/profile interactions
Assuming Leiningen is obsolete Misreading mature codebases Learn it well enough to contribute
Adding plugins casually Harder builds and slower startup Prefer simple project config first
Migrating to deps.edn before understanding the project Churn without value Match the repository’s current toolchain

Key Takeaways

  • Leiningen remains important because many real Clojure projects use it.
  • project.clj is the center of a Leiningen project.
  • lein repl is part of the development loop, not just a debugging tool.
  • Profiles and plugins can change project behavior substantially.
  • Java build-tool instincts transfer, but Clojure’s REPL-first workflow changes what “productive” feels like.

Quiz: Leiningen Basics

### What file is central to a Leiningen project? - [x] `project.clj` - [ ] `deps.edn` - [ ] `pom.xml` - [ ] `settings.gradle` > **Explanation:** Leiningen projects are organized around `project.clj`. ### Which command starts a project-aware REPL? - [x] `lein repl` - [ ] `lein jar` - [ ] `lein clean` - [ ] `lein deps.edn` > **Explanation:** `lein repl` starts a REPL with the project classpath and dependencies. ### Why should you avoid migrating a Leiningen project immediately? - [x] The existing toolchain may encode team workflow, plugins, profiles, and packaging behavior. - [ ] Leiningen projects cannot be run on the JVM. - [ ] `deps.edn` cannot express dependencies. - [ ] Clojure code only works with one build tool. > **Explanation:** Tool migration is a project decision, not a default onboarding step.
Revised on Saturday, May 23, 2026