Most engineers optimize for capability per tool. Pick the best tool for each job, they say. SQL for persistence, Python for scripting, Go for performance, React for UI, Terraform for infra, YAML for config. Ten tools. Ten cognitive models. Ten sets of footguns.

This is the standard view. It works. People ship software this way every day.

I think it’s wrong.


The Entropy Problem

Every tool you add introduces irreducible cognitive overhead:

The result is that your cognitive budget is drained before you solve the actual problem. You spend 60% of your capacity managing the toolchain and 40% building the product.

This is entropy. Every tool is a perturbation of the system. Over time, the system drifts toward chaos.

Collapsing entropy means removing layers. Not adding better ones.


The One Tool Constraint

What if you committed to exactly one general-purpose programming model and made it work for everything?

Not one language per layer. One language. Data layer, application layer, scripting, automation, web frontend, CLI, infra configuration, CI/CD pipelines, one-off analytics. Same paradigm, same syntax, same composability model, from the database to the browser to the shell prompt.

The constraint forces hard trade-offs:

Everything else — the full surface area of modern application development — collapses into one pipeline. One deprecation policy to track. One idiom across the stack. One way to compose, test, and deploy.

This is the entropy-minimizing attractor. Not the easiest path. Not the most performant. The one that leaves you with the most remaining cognitive budget for the actual problem.


Why Clojure

If the goal is maximum surface area from one tool, Clojure is the strongest candidate I’ve found. The specific properties that matter:

Data Orientation

Everything in Clojure is data — code, config, state, messages, queries, responses, tests. There is no special category of “code” that behaves differently from “data.” A function definition is a data structure. A macro is a function that transforms data structures at compile time. A test is a data structure that asserts properties about other data structures.

This collapses the meta-layer into the application layer. You don’t need a separate build system to generate code. You don’t need a template engine to transform text. You don’t need a DSL — you already have one, because your language is just data, and data can be composed at runtime.

Immutability by Default

No variable can be mutated. No object’s state can change after construction. Every function takes a value and returns a new value. The old value remains unchanged and reusable elsewhere.

This is not a performance optimization. It’s a trust optimization. When you read a line of Clojure code, you know exactly what it produces. There is no hidden state that a concurrent thread, a parent class, or a callback handler might have modified. The DAG of data flow is explicit and inspectable at every point.

This maps directly to how I think systems should work. In a DAG, a child node should never be able to reach back and modify its parent. Mutation is that backward edge. Immutability removes it.

No Inheritance

Object-oriented languages model relationships through inheritance: a Dog extends Animal adds bark(). The problem is that inheritance is not a DAG. A child class can override a parent method, altering the behavior of the entire parent abstraction from below. Polymorphism makes this worse — the same method call can produce different behavior depending on the runtime type of the receiver, which the caller cannot determine statically.

Clojure has no inheritance. Composition replaces inheritance: have a value, pass it through a function, get a new value. There is no parent to override. There is no child to surprise you. The flow is unidirectional.

The Host Superpower

Clojure runs on the JVM, JavaScript runtimes, and .NET/CLR. This means it inherits the largest library ecosystems in existence — Java’s Maven Central and JS’s npm — plus their production-runtime properties (JVM: battle-tested GC, monitoring, profiling, 20 years of production hardening; JS: ubiquitous browser runtime; .NET: Windows-ecosystem reach).

Additionally, Babashka (a native-compiled Clojure with fast startup) enables Clojure scripting for CI/CD, CLI tools, and automation. Single binary, no runtime dependency, sub-second startup. This closes the final gap — Clojure now competes with Python and bash for scripting tasks.

REPL as Dialogue

The interactive development environment (REPL) is not a debugger. It’s a real-time dialogue with the running system. You can connect to a production server, inspect its state, redefine a function, test the change, and disconnect — all while the server continues serving traffic. You can build a data pipeline incrementally, inspecting each transformation step before composing the next one. You can connect to a remote API with incomplete documentation and, in <30 seconds, issue a real request and inspect the response live.

This collapses the edit-compile-run-fail-debug loop into a single continuous feedback cycle.


LLMs Close the Last Gap

I don’t type code. I describe what I want, inspect the result, and refine the specification. The LLM handles syntax, boilerplate, and mechanical translation. I handle architecture, trade-off analysis, and verification.

This works because Clojure’s syntax is minimal and regular. There is no context-sensitive grammar, no operator overloading, no implicit coercion, no special case depending on how a function is called. The LLM can produce idiomatic Clojure with high reliability because the language has few surface-level surprises.

The combination gives me:

Layer Who Owns It
What to build Me
System architecture Me
Data model design Me
Security/compliance analysis Me
Syntax / boilerplate LLM
Tests (generative) Me (spec) + LLM (implementation)
Deployment / CI Babashka (I configure the skeleton, LLM fills details)

The result is that I ship more systems in less time than I could by typing code myself, without sacrificing architectural control. The bottleneck becomes clarity of specification, not typing speed.


What This Means in Practice

After adopting this model, here is what I shipped in 22 days:

None of this required me to type syntax. All of it required me to think about architecture, data flow, security, and composition.


The Trade-offs Are Real

This approach has limits. I should be explicit about them:

These are real constraints. They define which roles and environments are viable. For the domain I operate in — enterprise security, Bitcoin protocol, compliance automation, systems with strong formal boundaries — they are acceptable.


Conclusion

Collapse entropy to zero is a direction, not a destination. You will never eliminate all tools, all dependencies, all cognitive friction. But you can push toward a state where the remaining entropy is a conscious choice, not the accumulated debris of N years of tool-of-the-week decisions.

For me, the attractor state is:

This collapses the surface area of “developer environment” to a single Babashka binary and a terminal. Everything else — databases, APIs, CI/CD, cloud services — is infrastructure, not tooling. The cognitive budget that was spent on managing toolchains is redeployed to solving the actual problem.

One tool to rule them all is not a fantasy. It’s a constraint that forces every other choice toward simplicity. The only question is whether you’re willing to accept the trade-offs.