Versioning and Release Management

How to version Clojure libraries and applications, drive releases from Git, and automate builds with current Clojure CLI workflows.

Versioning and release management in Clojure is mostly about contract discipline. The language does not need a special theory of release engineering. What matters is whether your public API, artifacts, and deployment workflow make change predictable for the people who depend on your code.

Older Clojure content often over-focuses on Leiningen plugins or Travis CI recipes. Those can still appear in established codebases, but current official guidance is built around the Clojure CLI, deps.edn, and tools.build. That shift matters because it changes how teams should think about repeatable builds: less hidden plugin behavior, more explicit scripts and version sources.

Decide What the Version Means

Before choosing tooling, decide what a version communicates.

Libraries

For a reusable library, semantic versioning is usually the clearest default:

  • MAJOR for intentionally breaking public changes
  • MINOR for backward-compatible additions
  • PATCH for backward-compatible fixes

This matters most when:

  • you publish jars consumed by other teams
  • you expose macros, public vars, protocols, or data contracts
  • you expect automated dependency upgrades downstream

Applications

For a deployable service, the important thing is traceability. Many teams still use SemVer for applications, but calendar versions or Git-derived versions can also work if they map cleanly to deployed artifacts and incident timelines.

The weak choice is not SemVer versus calendar versioning. The weak choice is a version that nobody can connect to source, changelog, or deployment state.

Make Git the Source of Truth

A sound release pipeline usually starts with Git, not with a build plugin mutating version state in hidden ways.

Good release flow:

  1. merge reviewed code
  2. run tests and packaging in CI
  3. create an annotated tag for the intended release
  4. build the artifact from that exact commit
  5. publish artifact and notes together

That gives you one anchor for:

  • the code that shipped
  • the artifact that was produced
  • the changelog entry
  • rollback and incident analysis

For Clojure libraries, that is more important than whether the jar was produced by one tool or another.

Use Explicit Build Scripts

The current official build direction for new Clojure projects is the Clojure CLI plus tools.build. The key benefit is visibility: your build becomes ordinary Clojure code instead of opaque plugin configuration.

 1(ns build
 2  (:require [clojure.tools.build.api :as b]))
 3
 4(def lib 'com.acme/reporting)
 5(def version (or (System/getenv "RELEASE_VERSION")
 6                 "0.4.0-SNAPSHOT"))
 7(def class-dir "target/classes")
 8(def basis (delay (b/create-basis {:project "deps.edn"})))
 9(def jar-file (format "target/%s-%s.jar" (name lib) version))
10
11(defn clean [_]
12  (b/delete {:path "target"}))
13
14(defn jar [_]
15  (clean nil)
16  (b/copy-dir {:src-dirs ["src" "resources"]
17               :target-dir class-dir})
18  (b/jar {:class-dir class-dir
19          :jar-file jar-file
20          :basis @basis
21          :lib lib
22          :version version}))

This example is intentionally simple. In a real pipeline you would likely also:

  • run tests before packaging
  • generate or validate a changelog entry
  • refuse release versions from dirty worktrees
  • publish only from CI, not from a laptop

Separate Build Identity from Dependency Identity

One common mistake is conflating your application’s version with the versions of the libraries it consumes.

1{:paths ["src" "resources"]
2 :deps {org.clojure/clojure {:mvn/version "1.12.4"}
3        com.github.seancorfield/next.jdbc {:mvn/version "1.3.1048"}}}

These dependency versions matter for reproducibility, but they are not your release identity. Your release identity comes from the artifact you publish, the tag you cut, and the contract you maintain.

Legacy Tooling Still Exists, but It Should Not Drive the Lesson

You will still encounter:

  • project.clj
  • lein release
  • ad hoc shell scripts around lein uberjar

That is normal in older systems. The practical rule is:

  • support legacy release flow when maintaining an existing codebase
  • prefer CLI-driven, explicit, Git-centered release flow in new examples

The guide should teach the modern default without pretending older systems vanished overnight.

Release Practices That Hold Up Under Pressure

Strong Clojure release management usually includes:

  • one clear version source, usually CI input plus Git tags
  • automated tests before artifact publication
  • machine-readable artifact naming
  • human-readable release notes
  • changelog discipline for public libraries
  • rollback-friendly deployment metadata

For libraries, add one more question: what exactly counts as a breaking change? In Clojure that can include:

  • changing map keys or required options
  • altering exception shape or ex-data
  • renaming public vars or moving namespaces
  • changing macro expansion expectations
  • tightening protocol or record assumptions

If you do not treat those as release-contract questions, semantic versioning becomes decorative.

A Useful Mental Model

    flowchart LR
	    A["Reviewed Commit"] --> B["CI Validation"]
	    B --> C["Git Tag / Release Version"]
	    C --> D["Build Artifact"]
	    D --> E["Publish Artifact"]
	    E --> F["Release Notes and Changelog"]

The important point is sequence. A trustworthy release is not a jar that happens to exist. It is an artifact tied to a validated commit and a versioned release decision.

Key Takeaways

  • Use semantic versioning when consumers need clear compatibility signals, especially for libraries.
  • Let Git tags and CI define release identity rather than hiding version changes inside plugin behavior.
  • Prefer Clojure CLI plus explicit tools.build scripts for new build examples.
  • Treat data-shape changes, public vars, macros, and exception contracts as versioned surface area.
  • Optimize release flow for traceability, rollback, and downstream trust.

References and Further Reading

Ready to Test Your Knowledge?

Loading quiz…
Revised on Thursday, April 23, 2026