Clojure, Context and Jumping In

"Able and willing to help with this ticket?" a Senior Director slacked me. The ticket was simple. Find an internal field and expose it for the API. Ok, easy.

This API was in a Clojure codebase I had briefly touched a year ago. Clojure is a functional and dynamic language that uses a 'map' of key/values as the default. You often see a function takes an argument like data. Maybe some of the keys are used, or maybe just some keys are added, but you don't know what exists on that data object from code alone you have to run the code. It would help if you had a REPL to help explore that, and I didn't have the REPL working in this case.

The API took a subset of keys and exposed them via JSON. I didn't know the exact name of the key, so what I ended up having to do was back-trace to that map's creation. This process took 10 minutes. Various chained functions, extra transformations and protocols until I discovered the core data structure. I found the key/value that needed to be exposed and modified the API to whitelist that key which was a 1-minute change.

I like the conciseness of maps in Clojure, but going into a project blind can be challenging. One of my biggest frustrations with Clojure is data and context in functions. You don't know the data models a function uses. You have to start to pick up the entire context outside of the function or namespace you want to focus on. Additionally, there might be several undocumented transformations since they are only internal that you have to unknot.

In a typed language, this is easier. Typed languages force you to be explicit on data going into and out of a function. The context moves to the boundaries and signature. This encapsulation is an aspect I started to lean on more and more in Typescript(which is 'strongly' typed loosely) and other statically typed languages.

Clojure is the first functional language I took a deep look at. Using it professionally has taught me a lot about the importance of higher-order functions, immutability, a multitude of functions that can be composite together. In general, for Clojure, you need to focus on the meta-code like REPL and tests to get this context.

People coming from statically typed languages have trouble with this, and they need to look at that meta-code to see how Clojure addresses that problem differently. Why is REPL-driven development such a big deal with Clojure? Why is the main proponent of TDD such a big Clojure fan ? These are related, and it would help anyone coming into Clojure to try to understand this.