As Clojure daily users we deliver software systems for our clients in the toughest conditions: complex domain and demanding requirements, short delays, large organization, etc. (for example, read Michelin's feedback) We would like to share the reasons behind our choice of Clojure as our main programming language, discussing the key benefits of Clojure and why it became the cornerstone of our software studio without falling into a fanboy discourse, highlighting several years of experience running Clojure-based software in production.For an introduction to Clojure you can read the Clojure's Rationale that greatly explains its characteristics, then ACM article "history of Clojure" tells Rich Hickey's background and motivations in building such a language and goes into deeper details about Rich's language designer journey.Table of Contents
Clojure is powerful...combined with Software Engineering and Design Skills
Clojure is just a tool - a very powerful one - that gives developers a lot of freedom, but in a team with dozen of developers you need strong engineering discipline otherwise your code can mix different style and become a mess. Clojure is "only" a programming language, you need engineering practices and features for getting a production-ready software in the end:
The required disciplines is not the subject of this post but think about architecture style (Hexagonal architecture for the better) and the various concerns of a production-ready software (log, configuration, state management, metrics, API, data schema, persistence, security, etc.). That's why we maintain our own clojure tech radar and practices that we'll publish in the future. We'll dive into the language features that gives us this power at the very end of our post. We want to focus first on the benefits outside of the programming language features.We also practice Domain-Driven Design before writing any code to explore and understand the domain (Event Storming is a great style of workshop to run in the first place), write some scenarios in Gherkin to ensure the expected behavior is shared then start structuring the domain and code it. During the exploration time needed by design we do a lot of prototyping and that's when Clojure kicks in: its interactive and exploration capabilities combined with strong language features makes it the perfect tool for our craft.
Clojure is Data Oriented
It is better to have 100 functions operate on 1 data structure than 10 functions on 10 data structuresAlan J. Perlis, developer Algol language, 1st recipient of Turing Award
Few data structures / lots of functions. Data structures are deliberately few (Map, Set, Vector or List), functions are easily reused on the common data abstractions.
Clojure is THE language for manipulating data.
Homoiconicity: Code is Data, Data is code. Easy domain-specific language and code can be manipulated as data.
Persistent Data Structure & Immutability: no state sharing, easier to reason about.
Rich Data Literals:
enable interactive development and efficient feedback loop
Developer can SEE and manipulate their data
EDN: powerful serialisation/deserialisation with built-in or specific data readers
Pure functions (idempotent and without side-effect) increase reusability and allow to reason « locally » just considering the function at-hand (not considering prior or global state and all possible inputs)
Everything is an expression, easier to reason about, almost no statements nor complicated state management
Declarative approach and data literals
Clojure's data literals are rich and powerful. A data literal is a notation for representing data structure declaratively and directly in the programming language source code, instead of imperative invocations to construct the data structure such as in Java in the following code block:
Map m = new HashMap();
Data literals in Clojure works for scalars (Integer, String, etc.) and composite (Sequential and Hashed collections). The main benefits is readability and very low cognitive load to understand the shape of data through an example and moreover to translate the domain concepts directly in data structure. The bowling game example perfectly fits this objective:Here we can directly translate this sample to a data structure and develop the language of our domain, a frame is a tuple, a vector with two integer elements and game is ten tuples, each game is then associated to the player.
The data structure above can be read and understood directly with the lowest cognitive load possible, almost like the bowling game sheet showed previously.A score function takes a game as argument and returns the total and intermediate scores like this:
The data literals are also extensible with tagged elements and readers that converts a String representation to a in-memory structure...and that's perfect for DDD's Value Object (I prefer calling them Value Type because the goal is to construct a value, and the type is just the description of the structure and behavior associated to a value). Exemple: the code #inst "2018-03-28T10:48:00.000" doesn't return a String but an Instant java Object.We can express a domain example directly and start coding some behavior around sample data structures, and that leads to the next section, the data literals richness in Clojure makes the interactive programming experience really shines as the developer is then able to "see the data" directly in the source code and REPL (and not thanks to a debugger, a println or log output, a database visualizer, etc.).
Domain Exploration through Interactive Programming Experience
The Clojure developer experience is strongly linked to the editor and the famous REPL. This interactive environment puts us in a Flow state or "in the Zone" and there lies a lot of the productivity that come with Clojure. In short, the REPL and the associated editor allows very quick feedback loop. There is the REPL but the whole language contribute to a developer experience that's like no other: immutability, short pure function and moreover rich data literals. That experience enables tremendous developer productivity but also a lot of fun and joy!The interactivity of the developer experience impacts not only the "code construction" activity but also the debugging and code exploration activity during maintenance. Thanks to Clojure's pure function whose use is facilitated by the language, just invoking the function with some data is enough to debug. For complicated case we can even connect a REPL to a running system remotely and debug it (even if the system to debug is 150 million miles away).Clojure's dynamic nature and interactive development environment allow for rapid development cycles, quick code iteration, and a REPL-driven development approach. This enables our team to quickly build, test, and refine solutions, leading to faster delivery of high-quality software. Additionally, the ability to redefine functions on the fly makes debugging and code exploration a breeze.
Low developer cognitive load
There are several characteristics of Clojure that made the developer's cognitive load very low, hence increasing productivity by focusing on the domain at-hand only:
Pragmatic Functional Programming: the core of FP is to compose functions that transform data in a kind of pipeline. Functional programming emphasizes immutability, purity of functions, and the use of higher-order functions to manipulate data. This approach reduces side effects and promotes a good separation of concerns.
Pure-function and almost no global-state or complex state management: this allows "local-reasoning" as function works only on given arguments (Local reasoning is a property of some code wherein the correctness of the code can be inferred locally under specified assumptions, without considering prior application state or all possible inputs). Code reuse is a unique consequence of that characteristic, I often reuse code in some open-source libraries by just copy-pasting the function I'm interested in and just follow the dependencies in it, very often the functions in dependencies are very few and I can borrow some code very easily, a thing I seldom encounter in other languages.
No statements only expressions: every expression returns something (even side-effect one like println that returns nil), this force code to return something and to avoid state change, this makes it easy to evaluate and understand any piece of code given some data.
Very concise code: in the end, the verbosity of the code increases the developer's cognitive load, Clojure's conciseness makes it very easy to glance at a piece of functionality all at once. Clojure eliminates most template/boilerplate code: nearly all code that we write is directly related to a functionality.
Ecosystem, Community and recruitment
Clojure in Production
Building a system is easy but running it is the real test and Clojure also shines in this area:
Being hosted on the JVM, the tools are the same than an Ops would use for any other traditional Java system: we use Docker and Kubernetes hosted with the usual Cloud providers, JMX, Prometheus, etc.
The troubleshooting activity is somewhat special with Clojure: the functional nature of the code makes it very easy to reproduce an invocation context and troubleshoot a problematic piece of code, moreover we are also able to connect to a running system through a REPL and explore any part of it.
Clojure's macro capability is a powerful metaprogramming tool, that's to say it helps to write code that write code. With macros we are able to take any data structure, unevaluated, and transforms it the way we want to produce valid Clojure code. This capability is often used to produce repetitive code from a more concise version specific to the task at-hand (then called Domain-Specific Language). This ability to manipulate code as data, and vice versa, stems from the Lisp heritage of Clojure and is known as "homoiconicity" (a perfect word to shine in society!).
Why Building and Running Software with Clojure is cheaper?
No waste during development
Full efficiency of the developers, focus on the problem at-hand, free the developer potentiel to focus on its problem
Strong feedback loop and interactive development, developer always in a flow state
Very low cognitive load, always local reasoning
Prototyping is the fastest way to move forward in software development
Clojure community has a strong focus on software design discipline and the software engineering level of the people we meet in the community is very high.
During the Run phase:
Clojure allows easy reproductibility of the execution context when dealing with support case. Clojure makes it easy to reproduce a case’s execution context with light setup. No ceremonies, like in Java, to reproduce a support case.
For the toughest support case, we can even connect to a running system and inspect it interactively.
By choosing Clojure as the foundation for our software studio, we have embraced a powerful, elegant, and expressive programming language that allows us to efficiently build high-quality, maintainable, and scalable applications. The Clojure community emphasizes using libraries instead of frameworks and Clojure's code eliminates most template/boilerplate code: nearly all code is directly related to functionality. Clojure is a boring technology but very fun and joyful to use, it releases our full potential for solving problems, in our opinion having strong software engineering skills + using Clojure is a killer combination.The benefits of functional programming, the JVM ecosystem, and Clojure's rich data structures and concurrency model have proven invaluable in our projects. Combined with a thriving community, we are confident that our bet on Clojure will continue to pay dividends for our studio and the solutions we provide to our clients.Clojure is not "magic", fashionable or revolutionary, but it has a lot of small improvements and niceties which, taken together, result in a huge difference when writing real-world large and complex systems.
"The greatest single programming language ever designedAlan Kay, on Lisp
Lisp is worth learning for the profound enlightenment experience you will have when you finally get it; that experience will make you a better programmer for the rest of your days, even if you never actually use Lisp itself alot."Eric Raymond, How to Become a Hacker
One of the most important and fascinating of all computer languages is Lisp (standing for "List Processing"), which was invented by John McCarthy around the time Algol was invented.Douglas Hofstadter, Gödel,Escher,Bach
Within a couple weeks of learning Lisp I found programming in any other language unbearably constraining.Paul Graham, Road to Lisp
Lisp is the most sophisticated programming language I know. It is literally decades ahead of the competition...it is not possible (as far as I know) to actually use Lisp seriously before reaching the point of no return.Christian Lynbech, Road to Lisp