The Practice of Clojure

It’s been just over eight months since I switched from Ruby to Clojure for professional development. Clojure is a joy to use: I firmly believe that I’m writing the best software I’ve ever written.

But learning to use Clojure is not easy. When I switched from C# to Ruby, I felt more productive in a matter of weeks. It took me much, much longer to be productive in Clojure.

Why? There are many enlightening Clojure tutorials, but they tend to focus on how to write functions and use the standard library. However, when it came time to write more than toy programs, I found that understanding the language itself wasn’t enough to help me navigate the vast array of choices ahead of me. I wondered …

How do I write Clojure? What editor should I use? What are the best practices for debugging? For writing tests?

What language features do I use? Clojure has a lot of features and it can be overwhelming to understand the tradeoffs between them. Should I use atoms or refs? Should I use maps or records or types?

How do I compose parts to build bigger systems? Most tutorial focus on building small pure functions, but how do I put these together to solve real-world problems?

So, if you already understand the Clojure language, here’s my advice on the practice of Clojure - how to use it productively to build systems.

How do I write Clojure?

Pick an editor. Don’t spend hours fighting Emacs if you’re not already proficient. Although I don’t have first-hand experience, I’ve heard great things about Cursive and Light Table.

The single most important editor feature is REPL integration. Do not skip this step, even if it takes time get it working. Don’t settle for copying and pasting code into REPL. From within your editor, you should be able to:

Execute a single expression with a keyboard shortcut Load the entire namespace with another keyboard shortcut.

Seriously, spend the time to get this working, even if takes hours, because REPL-driven-development is a enormous boost to your productivity in Clojure.

After you’re a little more comfortable with RDD, you’ll find that restarting the REPL is painful. Spend some time to refactor your app to use Component to enable the “reloaded” workflow.

Ditch the debugger. I used to rely on the Ruby debugger, so it was hard to live without it at first. To be frank, it’s unfortunate that debugging in Clojure is not great. I’ve heard Cursive has a debugger, but don’t let the lack of a debugger in your editor stop you from moving forward. Try to make simpler pieces that you can reason about and experiment with them in the REPL as you go.

You won’t need to test as much as you do in an OO language. RDD gives you the fast feedback of TDD, but without adding a ton of artifacts. I usually do RDD and then pick a handful of the most illustrative tests to keep.

Use clojure.test . It might feel a little basic compared to, say, RSpec, but it gets the job done and there many complementary tools for it. I found the lack of mocks and stubs in clojure.test disorienting at first. Eventually I learned that when I wanted a fake, I usually could refactor the code to use pure functions, at which point the fake wasn’t necessary.

Learn test.check . For pure code, generative testing is an indispensable complement to example-based tests (e.g. traditional unit testing). I’d also argue it’s better documentation, because you’re providing more general rules about your code instead of giving examples and letting the reader infer the properties.

What language features do I use?

Avoid creating types. If you’re coming from an object-oriented language like I was, your first instinct may be to build lots of types. Fight this urge. You can get surprisingly far building up data with vectors and maps. When you do really want a type, use defrecord .

If you’re getting lost in a sea of maps and want some structure, use Schema. Start by adding as few types as possible. Schema works best if you validate along system edges (i.e. the data going to/from the database or over the network).

If you need polymorphism, start by using protocols. You can ignore multimethods for now.

You will need some mutable state. Most of the time, atoms will do the trick. You can ignore refs and agents for now.

Avoid local state and data hiding. Instead, keep a few global pieces of state. You need way less mutable state than you might think. In an OOP system, you might have tens or hundreds of objects in a graph. In a typical Clojure system, you’ll likely have zero to five atoms.

How do I compose parts to build bigger systems?

Break up your app into a minimal number of “components”. You can sort of think of them as big, long-lived objects, but you’ll probably only have 1-5 of these in your app, they are are all singletons, and they live for the life of your app. If they need to talk to each other, connect each of these components with core.async channels.

Wrapping up

I expect that those who have worked on different systems will disagree with some of my recommendations, so I’m eager to learn which tools and practices others recommend. If you’re just getting started, what else do you need to know to help you be effective with the practice of Clojure?

Let me know in the Reddit discussion

Further Reading/Watching: