Browse Clojure Foundations for Java Developers

Understanding Leiningen

Learn what Leiningen still does well, how project.clj works, and when Java developers should lean on it.

Leiningen is one of the oldest and most influential tools in the Clojure ecosystem. Even if you start new learning projects with the Clojure CLI and deps.edn, you will still encounter Leiningen in mature codebases, company projects, tutorials, and automation scripts.

For a Java developer, Leiningen feels familiar for good reasons:

  • one main project file
  • named tasks
  • project scaffolding
  • dependency management
  • packaging commands

That familiarity makes it a useful bridge into Clojure, even though it is not the only workflow you need to know.

What Leiningen Is Good At

Leiningen is a project automation tool organized around project.clj. It can:

  • create a project skeleton
  • resolve dependencies
  • run tests
  • start a project-aware REPL
  • run an application
  • build an uberjar
  • support plugins and profiles

If you come from Maven or Gradle, the mental model is not exact, but it is close enough to feel productive quickly.

project.clj Is The Center Of Gravity

A Leiningen project is usually centered on a project.clj file:

1(defproject my-app "0.1.0-SNAPSHOT"
2  :description "A sample Clojure app"
3  :dependencies [[org.clojure/clojure "1.12.4"]]
4  :main ^:skip-aot my-app.core
5  :profiles {:uberjar {:aot :all}})

You do not need to memorize every key at once. The practical ones to understand early are:

  • defproject for the project identity
  • :dependencies for libraries
  • :main for the entry namespace
  • :profiles for environment-specific behavior

That is already enough to read most small-to-medium Leiningen projects.

How It Differs From Maven And Gradle

The best comparison is not “Leiningen is Maven written in Lisp.” The better comparison is:

  • Maven: lifecycle-heavy and XML-driven
  • Gradle: task-oriented with a programmable build DSL
  • Leiningen: task-oriented, project-centric, and Clojure-native

You typically do not spend much time building custom build logic up front. Instead, you rely on a small set of well-known tasks:

1lein new app my-app
2lein test
3lein repl
4lein run
5lein uberjar

That is enough for many real applications.

Why Leiningen Still Matters

It is easy for newcomers to hear “the official docs use the Clojure CLI” and conclude that Leiningen no longer matters. That is a mistake.

Leiningen still matters because:

  • many existing projects use it
  • teams often have strong habits around lein repl, lein test, and lein uberjar
  • its templates and plugins remain useful
  • its project workflow is easy for Java engineers to grasp

So the correct posture is not “pick a side.” It is “be able to read and work in either workflow.”

Common Leiningen Commands

These are the commands a Java engineer should understand first:

lein new

Create a new project from a template:

1lein new app my-app

lein repl

Start a REPL with the project’s classpath and dependencies loaded:

1lein repl

This is often the fastest way to get an interactive session in an existing Leiningen codebase.

lein test

Run the test suite:

1lein test

lein run

Run the application’s main entry point:

1lein run

lein uberjar

Build a standalone jar with dependencies:

1lein uberjar

For Java developers used to packaging and deployment artifacts, this is one of the most immediately recognizable commands.

Profiles And Plugins

Leiningen becomes more powerful when you understand two extension points:

  • profiles for environment-specific configuration
  • plugins for extra project tasks and automation

You do not need to use them early, but you should know they exist because many production codebases rely on them.

This is one reason Leiningen has remained sticky in long-lived applications: it can grow with the project without forcing you into a deeply custom build script on day one.

When To Reach For Leiningen

Use Leiningen confidently when:

  • the repo already uses project.clj
  • the team already documents lein commands
  • you need the exact workflow built around Leiningen tasks
  • a plugin or template is part of the expected setup

Do not fight the codebase. Follow its toolchain first.

A Good Java-to-Clojure Translation

The mental shift is:

  • in Java, the build tool often feels separate from the language
  • in Clojure, the REPL, project config, and code-loading workflow are much more tightly connected

That is why lein repl matters so much. It is not just “open a console.” It is “enter the project’s running development environment.”

Common Mistakes

  • assuming Leiningen is only for beginners because it feels simpler
  • assuming a CLI-first project must be migrated to Leiningen for “serious builds”
  • editing project.clj mechanically without understanding which commands the team actually uses
  • ignoring the REPL and treating Leiningen only as a packager

Leiningen is most valuable when used as part of the inner loop, not only at release time.

Knowledge Check: Working Effectively With Leiningen

### 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 holds project metadata, dependencies, and task-related configuration. ### Which command gives you a REPL with the project's dependencies loaded? - [x] `lein repl` - [ ] `lein jar` - [ ] `lein deps` - [ ] `lein clean` > **Explanation:** `lein repl` starts a project-aware REPL, which is one of the most important commands in everyday development. ### Why does Leiningen still matter in current Clojure work? - [x] Many existing codebases and teams still depend on it. - [ ] It is the only way to run a Clojure REPL. - [ ] The Clojure CLI no longer exists. - [ ] It replaces the JVM. > **Explanation:** Leiningen remains relevant because many real projects, especially mature ones, still use its task model and project conventions. ### Which command is most directly associated with packaging a standalone application artifact? - [x] `lein uberjar` - [ ] `lein repl` - [ ] `lein new` - [ ] `lein help` > **Explanation:** `lein uberjar` builds a packaged jar including dependencies, which is a common deployment-oriented workflow. ### What is the best default when a team already uses Leiningen? - [x] Follow the established `lein` workflow first. - [ ] Rewrite the repo to `deps.edn` before contributing. - [ ] Avoid the project REPL and work only from standalone scripts. - [ ] Ignore `project.clj` and infer everything from source directories. > **Explanation:** Existing workflow conventions matter. The fastest path is usually to work effectively inside the toolchain the project already uses.
Revised on Friday, April 24, 2026