HackerReposA FIELD GUIDE TO OPEN SOURCE
ProSponsor the shelf♧ 0
← Back to top all-time
plumatic
schema
Clojure2013–2022
plumatic / schema

schema

Clojure(Script) library for declarative data description and validation

34Distinct people
40Mentions
1Explicit recommenders
2.5KGitHub stars
REPOSITORY AT A GLANCE

What to know before exploring

What it is

Clojure(Script) library for declarative data description and validation

GitHub

Where it is used

No documented product use was verified in the last source check.

What people are saying

I don't have nearly the breadth of experience in Clojure as the circleci guys, but I found that Prismatic's Schema [0] works well for me vs. core.typed. I can selectively…

numlocked used a recommendation phrase. One indexed comment matches. This is a phrase match, not a verdict.

No classified warning is attached to this page. Read the discussions before treating silence as approval.

Also in these discussions

Mentioned in the same Hacker News threads. That is not proof they are substitutes.

This week on Hacker News

Nov 7–Nov 14 UTC · Direct repository links and separately labeled related project stories. Indexed through 11/14/2026, 1:49:46 PM UTC.

No direct repository links were found in the indexed HN discussions over the past seven days. Older discussions are available below.

Mentions over time

HN mentions by year40 dated mentions

Observed mentions: 2013-09-05 16:49:14 UTC to 2022-10-05 02:30:45 UTC.

0369122013-01-01 00:00:00 UTC to 2014-01-01 00:00:00 UTC (exclusive): 2 mentions; 0 submissions, 2 comments20132014-01-01 00:00:00 UTC to 2015-01-01 00:00:00 UTC (exclusive): 6 mentions; 0 submissions, 6 comments2015-01-01 00:00:00 UTC to 2016-01-01 00:00:00 UTC (exclusive): 11 mentions; 0 submissions, 11 comments2016-01-01 00:00:00 UTC to 2017-01-01 00:00:00 UTC (exclusive): 8 mentions; 0 submissions, 8 comments2017-01-01 00:00:00 UTC to 2018-01-01 00:00:00 UTC (exclusive): 2 mentions; 0 submissions, 2 comments20172018-01-01 00:00:00 UTC to 2019-01-01 00:00:00 UTC (exclusive): 2 mentions; 0 submissions, 2 comments2019-01-01 00:00:00 UTC to 2020-01-01 00:00:00 UTC (exclusive): 2 mentions; 0 submissions, 2 comments2020-01-01 00:00:00 UTC to 2021-01-01 00:00:00 UTC (exclusive): 4 mentions; 0 submissions, 4 comments2021-01-01 00:00:00 UTC to 2022-01-01 00:00:00 UTC (exclusive): 1 mentions; 0 submissions, 1 comments2022-01-01 00:00:00 UTC to 2023-01-01 00:00:00 UTC (exclusive): 2 mentions; 0 submissions, 2 comments2022

Counts come from the repository’s dated source evidence. Index cutoff: 2026-11-02 08:16:59 UTC.

View mention counts and date ranges
UTC ranges include their start and exclude their end. Select a period to filter the source evidence.
PeriodStart (UTC)End (exclusive, UTC)MentionsSubmissionsComments
20132013-01-01 00:00:00 UTC2014-01-01 00:00:00 UTC202
20142014-01-01 00:00:00 UTC2015-01-01 00:00:00 UTC606
20152015-01-01 00:00:00 UTC2016-01-01 00:00:00 UTC11011
20162016-01-01 00:00:00 UTC2017-01-01 00:00:00 UTC808
20172017-01-01 00:00:00 UTC2018-01-01 00:00:00 UTC202
20182018-01-01 00:00:00 UTC2019-01-01 00:00:00 UTC202
20192019-01-01 00:00:00 UTC2020-01-01 00:00:00 UTC202
20202020-01-01 00:00:00 UTC2021-01-01 00:00:00 UTC404
20212021-01-01 00:00:00 UTC2022-01-01 00:00:00 UTC101
20222022-01-01 00:00:00 UTC2023-01-01 00:00:00 UTC202

From the project README

An excerpt from the repository, fetched Nov 13, 2026.

Read the project’s introduction
A Clojure(Script) library for declarative data description and validation. API docs. -- One of the difficulties with bringing Clojure into a team is the overhead of understanding the kind of data (e.g., list of strings, nested map from long to string to double) that a function expects and returns. While a full-blown type system is one solution to this problem, we present a lighter weight solution: schemas. (For more details on why we built Schema, check out this post.) Schema is a rich language for describing data shapes, with a variety of features: Data validation, with descriptive error messages of failures (targeted at programmers) Annotation of function arguments and return values, with optional runtime validation Schema-driven data coercion, which can automatically, succinctly, and safely convert complex data types (see the Coercion section below) Other Schema is also built into our plumbing and fnhouse libraries, which illustrate how we build services and APIs easily and safely with Schema Schema also supports experimental clojure.test.check data generation from Schemas, as well as completion of partial datums, features we've found very useful when writing tests as part of the schema-generators library Meet Schema A Schema is a Clojure(Script) data structure describing a data shape, which can be used to document and validate functions and data. (ns schema-examples (:require [schema.core :as s :include-macros true ;; cljs only ])) (s/defschema Data "A schema for a nested data type" {:a {:b s/Str :c s/Int} :d [{:e s/Keyword :f [s/Num]}]}) (s/validate Data {:a {:b "abc" :c 123} :d [{:e :bc :f [12.2 13 100]} {:e :bc :f [-1]}]}) ;; Success! (s/validate Data {:a {:b 123 :c "ABC"}}) ;; Exception -- Value does not match schema: ;; {:a {:b (not (instance? java.lang.String 123)), ;; :c (not (integer? "ABC"))}, ;; :d missing-required-key} The simplest schemas describe leaf values like Keywords, Numbers, and instances of Classes (on the JVM) and prototypes (in ClojureScript): ;; s/Any, s/Bool, s/Num, s/Keyword, s/Symbol, s/Int, and s/Str are cross-platform schemas. (s/validate s/Num 42) ;; 42 (s/validate s/Num "42") ;; RuntimeException: Value does not match schema: (not (instance java.lang.Number "42")) (s/validate s/Keyword :whoa) ;; :whoa (s/validate s/Keyword 123) ;; RuntimeException: Value does not match schema: (not (keyword? 123)) ;; On the JVM, you can use classes for instance? checks (s/validate java.lang.String "schema") ;; On JS, you can use prototype functions (s/validate Element (js/document.getElementById "some-div-id")) From these simple building blocks, we can build up more complex schemas that look like the data they describe. Taking the examples above: ;; list of strings (s/validate [s/Str] ["a" "b" "c"]) ;; nested map from long to String to double (s/validate {long {String double}} {1 {"2" 3.0 "4" 5.0}}) Since schemas are just data, you can also def them and reuse and compose them as you would expect: (def StringList [s/Str]) (def StringScores {String double}) (def StringScoreMap {long StringScores}) However, we encourage you to use s/defschema for this purpose to improve error messages: (s/defschema StringList [s/Str]) (s/defschema StringScores {String double}) (s/defschema StringScoreMap {long StringScores}) What about when things go bad? Schema's s/check and s/validate provide meaningful errors that look like the bad parts of your data, and are (hopefully) easy to understand. (s/validate StringList ["a" :b "c"]) ;; RuntimeException: Value does not match schema: ;; [nil (not (instance? java.lang.String :b)) nil] (s/validate StringScoreMap {1 {"2" 3.0 "3" [5.0]} 4.0 {}}) ;; RuntimeException: Value does not match schema: ;; {1 {"3" (not (instance? java.lang.Double [5.0]))}, ;; (not (instance? java.lang.Long 4.0)) invalid-key} See the More examples section below for more examples and explanation, or the custom Schemas types page for details on how Schema works under the hood. Beyond type hints If you've done much Clojure, you've probably seen code with documentation like this: (defprotocol TimestampOffsetter (offset-timestamp [this offset] "adds integer offset to stamped object and returns the result")) (defrecord StampedNames [^Long date names] ;; a list of Strings TimestampOffsetter (offset [this offset] (+ date offset))) (defn ^StampedNames stamped-names "names is a list of Strings" [names] (StampedNames. (str (System/currentTimeMillis)) names)) (def ^StampedNames example-stamped-names (stamped-names (map (fn [first-name] ;; takes and returns a string (str first-name " Smith")) ["Bob" "Jane"]))) Clojure's type hints make great documentation, but they fall short for complex types, often leading to ad-hoc descriptions of data in comments and doc-strings. This is better than nothing, but these ad hoc descriptions are often imprecise, hard to read, and prone to bit-rot. Schema provides macros s/defprotocol, s/defrecord, s/defn, s/def, and s/fn that help bridge this gap. These macros are just like their clojure.core counterparts, except they support arbitrary schemas as type hints on fields, arguments, and return values. This is a graceful extension of Clojure's type hinting system, because every type hint is a valid Schema, and Schemas that represent valid type hints are automatically passed through to Clojure. (s/defprotocol TimestampOffsetter (offset-timestamp :- s/Int [this offset :- s/Int])) (s/defrecord StampedNames [date :- Long names :- [s/Str]] TimestampOffsetter (offset [this offset] (+ date offset))) (s/defn stamped-names :- StampedNames [names :- [s/Str]] (StampedNames. (str (System/currentTimeMillis)) names)) (s/def example-stamped-names :- StampedNames (stamped-names (map (s/fn :- s/Str [first-name :- s/Str] (str first-name
Read the full README on GitHub

What Hacker News says

40 sources
I don't understand this reputation either. There are very large systems built on other Lisps, and they didn't collapse. What makes Clojure different? For example, Emacs has a massive amount of Elisp. Elisp is much more primitive than Clojure, and traditionally libraries don't use e.g. data schemas [1] as runtime contracts for data. Obviously, once a system built on top of a dynamic language grows beyond certain threshold, you need to be very disciplined as there are no static types to ensure some degree of correctness. With that said, it would be interesting to have a language from the ML family as a viable contender. Perhaps a modernized version of Standard ML. Or perhaps OCaml, if it gains some traction with multicore and algebraic effects. F# is nice, but it's a bit of a watered down version of SML due to the lack of functors. Scala is
Read the rest
too complex and far from ML in some regards. Haskell is good, but lacks critical developer mass and some libraries / laziness overcomplicate certain usecases. [1] https://github.com/plumatic/schema
In Building a Startup on Clojure
> When revisiting old projects I have seen countless of Clojure dependencies having died in the meantime. I have replaced utility libraries with more recent utility libraries sometimes several times. Very annoying. Yes, the "best in class" libraries change every few years in Clojure's universe, but they always bring huge improvements that require completely new APIs. For instance: https://github.com/plumatic/schema -> https://github.com/metosin/malli https://github.com/juxt/bidi -> https://github.com/metosin/reitit https://github.com/bhauman/lein-figwheel -> https://github.com/thheller/shadow-cljs https://github.com/stuartsierra/component -> https://github.com/weavejester/integrant https://github.com/technomancy/leiningen -> https://github.com/practicalli/clojure-deps-edn > So overall Clojure is in a strange situation. For picking it up o
Read the rest
n-the-job its not the responsible choice probably for most situations I work in. For my private tinkering the JVM dependency feels just a little too heavyweight. I don't see that Clojure will hit an inflection point of adoption in any way in the future that would make it a viable candidate for me to use at work. I think about programming languages as tools for different bottlenecks. – Computational: Rust – Networking: Go – Business logic: Clojure / Python And for many people Python has everything that Clojure is missing: a very low barrier to enter, and a very stable and well-documented ecosystem of frameworks and libraries.
In Clojure needs a Rails
One can check that something is a string and not a blank one. There are two different libraries that do it: https://github.com/plumatic/schema/blob/ddb54c87dea6926c6d73... https://github.com/clojure/spec.alpha/blob/eb49d429e85b6878a... Honestly I highly suspect that many, many "typing" solutions out there are plain ignorant of the whole spectrum of choices one can make, are tend to lean towards Java-like APIs out of that ignorance. This is not limited to Ruby, I also see it in TypeScript which is very much a contrived system for real-world usages while making little use of JS's dynamism.
In RBS, Ruby’s new type signature language
Not sure we have any talks about that specifically, but what I can say is that on the Clojure codebases: - We rely heavily on testing (unit tests, property-based/generative and integration) - We adopt a micro-services architecture, so most codebases are small enough that refactoring is easy, or sometimes we just replace the service altogether - We leverage Schema (https://github.com/plumatic/schema) and/or clojure.spec (https://clojure.org/guides/spec) to annotate functions and data schemas, but it's opt-in - We have static checking of data schemas across boundaries (e.g. checking data producers did not break consumers over HTTP or Kafka), which is where we found the most value (replaced complicated end-to-end tests)
In Nubank acquires Cognitect
Perhaps. I was trying to find something pithy to give as an example. It's difficult to express all the things about spec that feel off to me. I'll try to enumerate some: - verbosity (I think schema does this better https://github.com/plumatic/schema) - because it tries to not be about validating types of inputs and outputs, and rather treats everything as predicates, it tends to live in a grey area between type validation and unit-testing, and I think hinders both - it only helps at run-time - over-abundance of macro-usage
In State of Clojure 2020
I have this strange feeling, that first half-hour was totally unnecesary for what the sencond half is. I mean, that it feels that he have to rant always about. Like ok, this ideas are wrong, this is my idea that is totally new(not really) and is the right thing to do. Nowadays, that ranting about Java is not so cool, he have to rant about types. Only to finish saying that he is going to add to Spec something that have already been implemented in Clojure https://github.com/plumatic/schema. So at the end of the talk, what's the point?
In Maybe Not [video]
> finding the "joy" of dynamically typed languages and all the "freedom" they give you ... verifying those types at compile-time was actually pretty useful Indeed one of the "joys" is that one can apply that verification a la carte, and possibly with different approaches [0][1]. Similarly, one may desire to dispatch on type [2], but might sometimes want arbitrary dispatch [3]. > After writing the 10,000th unit test that only exists to ensure that you spelled some function/method/hash-key correctly This too is solvable with a la carte tooling [4]. Everything has trade-offs. Static type systems give a fixed set of benefits with a fixed set of costs. Some folks prefer to use their experience and judgement to choose when, where, and how to pay the associated costs (and that freedom of choice itself has a cost). It's okay to have different pre
Read the rest
In A Year in Clojure
>There is zero chance anyone would of been able to be as productive as I was yesterday if that code base was dynamically typed. Not even close. My team did a big refactor on our ClojureScript code base last week. We've been developing the project for over a year now. We had no trouble with type refactoring. We use Schema (https://github.com/plumatic/schema) to validate the data at the edges, and the REPL to run code as we write it. Type errors simply have never been a problem for us.
In Static typing will not save us from broken software
This is an important move for the Clojure language. As with all dynamic languages, and exarcerberated by the fact that it encourages relying on data first - as opposed to say, classes in other languages - Clojure puts the onus on the developer to catch errors in data. The clojure community as a whole embraced prismatic's schema as a way to provide occurence typing for data. Since library authors often tend to reduce their dependency surface, most libraries do not ship with it though. With the inclusion of this in clojure core, it will now be possible to provide occurence typing at the edge of functions instead of tracking malformed input deep inside apps. The racket-type contract notation is also a very welcome change from other similar approaches in my opinion. [1]: https://github.com/plumatic/schema
In Clojure.spec – Rationale and Overview

Showing the 15 most recent of 40 sources. Enable JavaScript to search and browse every indexed mention.

MAKE YOURSELF AT HOME

A few shortcuts

/
Search the collection
?
Open this guide
Esc
Close dialogs and navigation

Your saved repositories and interests stay in this browser.