You wouldn’t use a toothpick to wear down a mountain. You wouldn’t wear your trainers instead of your racing spikes at a track meet. So why would you use a hand saw to build an infrastructure when you need a scalpel for surgical precision?
Up to this point in the series, we’ve talked about embracing failure, experimenting more and moving faster. We've talked about the "why," now it's time to address the "how."
We’ve previously touched on the concepts needed to achieve an antifragile organization. Recapping the key ingredients:
- An architecture that lets you limit the scope of damage when something occurs, by creating isolated failure domains
- A team structure with enough autonomy that people can move at different speeds at different times
- Infrastructure that makes it safe to fail (infrastructure as a service or platform as a service)
- A continuous delivery pipeline so that you can rapidly get fixes out while exposing just a fraction of your users to your latest changes
- Tools and technology that reduce inertia
- A mindset that integrates IT and business and uses change as a weapon against competitors
In this post, you're going to see some tools and technologies that reduce inertia. I'm going to name some names. It shouldn't surprise you that some of these are technologies we create... after all, we build the things according to our philosophy. Wouldn't it be strange if the things we built didn't embody those values?
Sharp tools have these characteristics:
- They allow concise expression of ideas.
- They allow us to compose ideas to get higher and higher leverage.
- They reduce special-casing and make the general solution easier than the specific one.
- They flatten data and metadata into a common level. (This is metacircularity for data.)
Sharp tools let you treat code as disposable inventory, reducing risk and inertia.
We don't see Forth in the enterprise much. In the embedded systems world, though, it is a frequent choice for low-level firmware. A Forth program starts with a handful of "words." The programmer creates new words from existing ones until the final program is one magnificent utterance. Forth programs are all about composition. It's a stack-based language with no type checking, which means words can be composed without "glue" code (other than some SWAPs and DUPs). Separation of concerns is achieved by isolating a concern and offering a small set of words to manipulate it. A higher-level word will combine concerns and offer its own composable interface.
Ruby on Rails
Though we don't currently work with Ruby on Rails, collectively we have delivered many, many Rails applications. It has this wonderful combination of clear architecture and expressive language. Add the benefits of a vibrant community of gem authors, and you get the ability to create applications very quickly. Rails excels at certain types of applications. It is particularly good when building standalone applications backed by databases.
We've found there are what you might call "soft limits" to Rails' applicability. Enterprise applications in Rails suffer from the number of backend integrations needed. These also become high-inventory, golden anchor applications. I think such an application would last longer if the host company were more willing to rip and replace pieces of it.
An old study says the number of production-quality debugged lines of code a programmer can produce per day is roughly constant across different languages. If you’re working in assembly language, 15 lines of code is 15 instructions. Working in Java, 15 lines of code is three methods on a class. In Clojure, 15 lines is most of a microservice.
It's not just about the lines of code, though. The bigger benefit comes from composability. The bottom-level functions in Clojure don't seem to be very powerful at first glance. When you start composing them they get interesting, particularly using higher-order functions. The Clojure programmer's design challenge is to build similarly composable abstractions within their application's domain.
You can begin to see the impact of using sharp tools. The cost to write a service in Clojure is significantly less than writing that same program in Java. That lets you cut the anchor loose.
Java Versus Clojure Up Close
Here’s a quick look at the difference between coding in Java and Clojure, using a simple address book example. All I am doing here is building something in memory that keeps track of names and locations. Even if you know nothing about code, you can see the difference in lines of code it takes to accomplish the same thing.
Which of these two looks like code that you should keep forever? Remember that "keep forever" is a bad attribute. It means you perceive the code as an asset. The Java version certainly looks like a bigger investment. It takes more time to write and more effort to get right. It looks like the kind of thing you should save and reuse. That's how inventory builds up.
A passionate Java advocate might reasonably object that this is cheating. After all, Clojure can just declare a map as a literal. It doesn't need to have a series of add calls after the constructor. Yes, that's true. That's exactly the point! Using a language with higher leverage saves time and lines of code.
Part of what makes Datomic a sharp tool is that it lets you eliminate entire layers of code that other architectures must incorporate. An example is the object relational mapping layer at the heart of most applications. With Datomic, you simply don’t need this layer at all. (Ironically, one of the first things a programmer new to Datomic attempts is to build their own ORM. We've had three or four such attempts in Cognitect. Each one eventually discovers that their ORM actually restricts flexibility that you would otherwise have through ordinary data structures and functions in your code and regular Datomic attributes on untyped entities.)
Datomic also lets you eliminate the difference between defining the schema, defining queries, and defining facts in the schema. In Datomic, you treat these uniformly, where other databases treat them as three entirely different languages. This is homoiconicity for data.
Using Datomic, you overcome what’s commonly referred to as the impedance mismatch. This concept refers to the way that relational databases and object oriented programs inhabit two completely different paradigms—the part in your app where you need to bridge between those two paradigms is always, messy, complex and difficult to change. Datomic eliminates that entire layer of code, eliminating the impedance mismatch. This is what gives you leverage.
Antifragility Depends on Sharp Tools
Sharp tools are an essential ingredient of the antifragile organization. Sharp tools, such as Clojure and Datomic, let you cut loose of the boat anchor and shed inertia as you go, allowing you to build faster and treat more of your code as disposable inventory.
A common objection to using sharp tools is that, just as one person can do more good, it also means one person can do much more damage. In the next post, we'll look at how team-scale autonomy works together with failure-limiting architecture to make sharp tools safe.
Read all of Michael Nygard's The New Normal series here.