Library / SDK
taoensso/nippy avatar
taoensso/nippy

nippy: A Fast Serialization Library for Clojure

Fast serialization library for Clojure

1,112 stars63 forksClojureEPL-1.0

At a glance

What is it?
Nippy serializes Clojure values to bytes without going through the Clojure reader, and it is used in production by Carmine, Faraday, XTDB, Onyx, PigPen and Datalevin. Its most distinctive feature is a deliberate two release rule for new types, so a rolling deployment never has a writer talking to a reader that cannot decode it.
Who is it for?
nippy fits a Clojure service that is storing or moving large amounts of data and is hitting the reader's speed, and it fits a team that has to roll out a new version of a service while old instances are still writing data.
Can I use it commercially?
Yes, with conditions. EPL-1.0 is a weak copyleft licence: you can use it inside commercial and closed-source software, but if you distribute changes to its own files, you must publish those changes under the same licence.
Is it still maintained?
Yes. The repository last received commits 22 days ago.
What is it written in?
Mainly Clojure, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 22, 2026, and from our analysis. They are not legal advice.

Editorial analysis

A Drop-In Alternative to the Reader

The argument for Nippy starts with a limitation of the Clojure reader. Clojure's rich data types are the reason to use Clojure, and its reader allows you to take your data just about anywhere, but the reader can be painfully slow when you have a lot of data to crunch, like when you are serializing to a database.

So Nippy is positioned as a mature, high-performance drop-in alternative to the reader. The word drop-in is doing the work, because the alternative to a drop-in replacement is rewriting the call sites throughout a codebase, and the format is the reason the reader is slow.

The adoption evidence in the README is a list of projects that use it at scale: Carmine, Faraday, PigPen, Onyx, XTDB and Datalevin, among others. Carmine and Faraday are the author's own libraries, and the others are independent, which is the more interesting half of the list.

The library is small and simple and pure Clojure, which means it has no native dependency and no JVM flags to set, and it is mature and widely used in production for 12 or more years, which for a serialization format is the property that matters most.

The API Is Two Functions

The quick example in the README is the whole interface:

clojure
(require '[taoensso.nippy :as nippy])

;; Freeze any Clojure value
(nippy/freeze <my-value>) ; => Serialized byte[]

;; Thaw the byte[] to get back the original value:
(nippy/thaw (nippy/freeze <my-value>)) ; => <my-value>

Two verbs, freeze and thaw, and the round trip is written as a single expression so the symmetry is obvious. The naming is worth pausing on, because it implies the important property. A freeze is not a dump of a data structure that you might read back with a different tool, it is a snapshot with a format that the library promises to keep readable.

Everything else in the library is a variation on those two calls. The wiki covers getting started and usage, and the API reference is on cljdoc, and there is a tools namespace for easy and robust integration into third party libraries, which is how a library like this gets adopted by projects that only need one direction of the round trip.

There is also a thaw transducer, described as powerful and for flexible data inspection and transformation, which is the feature that turns a byte array back into something you can query rather than something you can only print.

Type Coverage, Custom Types and the Fallbacks

The feature list is where the compatibility questions get answered, and there are three separate promises in it.

First, comprehensive support for all standard data types, and a robust test suite that includes coverage of every supported type, which is the combination that makes a serializer trustworthy. A serializer that handles the common types and quietly drops the rest is worse than one that refuses.

Second, easy extensibility to custom data types, so a project with its own record type or its own tagged value can define how that type is written rather than converting it to something the library already knows.

Third, the fallbacks. There is an optional automatic fallback to Java Serializable for safe types, and an optional automatic fallback to the Clojure Reader including tagged literals. Those are the escape hatches: a type Nippy does not natively support can be routed through Java's serialization or through the reader, at the cost of the performance that made Nippy worth using for that type in the first place.

So the design is a fast path for the common cases and a correct but slower path for everything else, and the type allowlist for the Serializable fallback is a configuration variable rather than a fixed behaviour.

Compression and Encryption Are Optional Layers

Two optional transformations sit on top of the serialization, and both are described as optional in the feature list, which is the important word.

The first is smart compression with LZ4 or Zstandard. The library is at the level where you are already storing the bytes somewhere, so the question of whether a blob should be compressed before it goes to the object store or the database is a real one, and the two codecs are the sensible answers: LZ4 for speed and Zstandard for ratio. Making it optional means a caller can decide per payload rather than accepting a global policy.

The second is encryption with AES128. That is a different kind of optional, because encryption is the kind of feature that a library should not turn on silently, since the key management is not the library's business. The wiki has a section on encryption, and a reader looking at the feature list should read it rather than assuming the defaults.

What the library does not do is pretend to be a key management system. The keys, the rotation and the storage of the key material all stay with the application, which is the only arrangement that is honest about where the risk lives.

The Two Release Rule for Rolling Deployments

The operational considerations section is the most valuable part of the README, and the first item under it is data longevity.

Nippy is widely used to store long-lived data and promises, as always, that data serialized today should be readable by all future versions of Nippy. But the converse is not generally true. Nippy vX should be able to read all data from Nippy vY<=X, so that is backwards compatibility, and Nippy vX may or may not be able to read all data from Nippy vY>X, so forwards compatibility is not promised.

That is the shape every serializer ends up with, and stating it plainly is rarer than it should be.

The second item is the one that shows the author has operated this at scale. From time to time, Nippy may introduce support for serializing new types, or optimizations to the serialization of pre-existing types. To help better support rolling deployments and rollback, such changes always come over two version releases. Release 1 adds read support for the new types, and release 2 adds write support for them.

The consequence for an operator is stated directly: if you use rolling deployments, update step by step, for example v3.5 to v3.6 to v3.7, so that read support is fully deployed before the corresponding writes might happen.

A new instance writes the new format only after every instance can read it, and the rollback path stays open, because the old version can still read what the new one wrote.

Byte Output Is Explicitly Not Stable

The third operational item is the one that saves people from a bad afternoon.

It has never been an objective of Nippy to offer predictable byte output, and the recommendation is to not depend on specific byte output. The reasoning given is that a small minority of users do have specialized needs in this area, and since Nippy v3.4 the release notes will always clearly indicate if any changes to byte output are expected.

That is a careful position, because a serializer that promises stable bytes cannot change its output format at all, and a serializer that promises nothing about bytes is free to improve its encoding. The compromise is to be honest about the output and to warn in the release notes, which is what the v3.4 change amounted to.

The practical consequence is that you must not use the bytes as a fingerprint, a checksum surrogate, or a cache key derived from the serialized form, because the same value can produce different bytes in different versions, and even in the same version with different options such as compression enabled. If you need a stable identifier for a value, that identifier has to be computed by your code.

Who Depends on It and How the Project Is Built

The library is mature and widely used in production for 12 or more years, and the project page is at taoensso.com/nippy, with the API on cljdoc and a wiki for getting started and usage. The benchmark is in the repository as a Clojure file with the results as an image, and the README invites pull requests to include other alternatives in the bench suite, which is a rare and welcome invitation for a library whose main selling point is a performance claim.

The repository layout shows a mature Clojure project. There is project.clj for Leiningen, bb.edn and a bb/ directory for Babashka, src/ and test/ for the code, doc/ for the API documentation, wiki/ for the wiki content, and resources/. The CHANGELOG.md, SECURITY.md and FUNDING.yml are all present, and there is a start-mcp.sh script at the root, which suggests the project exposes something to model context protocol tooling.

The test setup is worth noting because of the badges. There are two CI workflows, one for the Clojure tests and one for Graal tests, so the library is verified on both the standard JVM and GraalVM, which matters for a serialization library that ends up in JVM-agnostic deployments.

The release cadence has been active this year, with v3.9.0-beta1 on 2026-08-06, v3.9.0-RC1 on 2026-08-21 and v3.9.0 on 2026-09-06, and the last push was on 2026-10-01. The licence is EPL 1.0, the same as Clojure, copyright 2012 to 2026 Peter Taoussanis.

Editorial conclusion

nippy fits a Clojure service that is storing or moving large amounts of data and is hitting the reader's speed, and it fits a team that has to roll out a new version of a service while old instances are still writing data. It is a poor fit if you need byte-identical output across versions, because the project states that predictable byte output has never been an objective and recommends against depending on it, and it is a poor fit if you need to read data written by a future version, since forward compatibility is explicitly not guaranteed. Before adopting it, read the data longevity section, and if you run rolling deployments, step the version up one release at a time rather than jumping. The library is licensed under EPL 1.0, the same as Clojure, the newest release is v3.9.0 from 2026-09-06, and the last push was on 2026-10-01.

Frequently asked questions

What is Nippy and why would I use it instead of the Clojure reader?

Nippy is a fast serialization library for Clojure that acts as a drop-in alternative to the reader, because the reader can be painfully slow when you have a lot of data to crunch, such as when serializing to a database. It is used at scale by Carmine, Faraday, PigPen, Onyx, XTDB and Datalevin.

What API does Nippy expose?

Two main functions, freeze to serialize any Clojure value into a byte array and thaw to turn it back. There is also a thaw transducer for flexible data inspection and transformation, and a tools namespace for integration into third party libraries.

Can I upgrade Nippy across several versions without downtime?

Yes, and the README asks you to do it step by step. New types and serialization optimizations are introduced over two version releases, first with read support and then with write support, so if you use rolling deployments you should update step by step, for example v3.5 to v3.6 to v3.7, so read support is fully deployed before the writes happen.

Is Nippy's byte output stable across versions?

No. It has never been an objective of Nippy to offer predictable byte output, and the recommendation is to not depend on specific byte output. Since v3.4 the release notes will always clearly indicate if changes to byte output are expected.

Does Nippy support compression and encryption?

Yes, both are optional. Compression can be smart with LZ4 or Zstandard, and encryption is available with AES128. There is also an optional automatic fallback to Java Serializable for safe types and an optional fallback to the Clojure Reader including tagged literals.

Official sources

  1. License: EPL-1.0
  2. Project website
  3. README
  4. Releases
  5. taoensso/nippy on GitHub
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/taoensso-nippy.svg)](https://hysenlabs.com/projects/taoensso-nippy)

Was this useful?