“Everything should be made as simple as possible, but not simpler” – Einstein
The Meaning of Simplicity
“Simplicity” means different things to different people depending on the context of the problem you’re trying to solve. Avdi provides a useful typology of cases in which the term “simplicity” is often used (and abused): minimizing unnecessary effort, hiding complexity, avoiding difficult-to-understand features, avoiding formal architecture, elegance of design, staying close to the domain.
I’ll spare you the details but I would highly recommend reading the full post. One of the points that I tend to see quite often in various discussions these days is the use of simplicity to justify new language choice by measuring the number of lines of code, elegance as a complexity measurement:
“sometimes when you choose to do less you force someone else to do more. And sometimes that someone else is you, two weeks down the road”
In other words, reducing the number of lines of code (elegance of code) is only one measure of complexity. Other measures include existing skillset, learning curve, available toolset, maturity, interoperability with legacy and other third party tools, etc. Choosing a certain language because it will reduce the amount of code may seem to be logical for a specific area of our application, but at the same time may not be the right choice if you take a broader perspective and look at the entire application or project. At that level, introducing a new language can actually end up introducing huge complexity.
Scalability & Simplicity
Scalability is one of the areas in which I could see almost all sort of the simplicity arguments being abused to justify a certain architectural choice. I have often used Twitter as an illustration for this type of thinking. For example, one of the more critical decisions in any product is the selection of the application framework. Twitter was well known for its selection of Ruby. Simplicity (and productivity which is essentially a synonym of simplicity) was used as the main argument for selecting Ruby vs. Java or any other framework at the time. Twitter made a very similar simplicity argument to justify the selection of Java and Scala over Ruby a year later. It would seem that the term “simplicity” was used to justify a certain choice, and then the exact argument was made to justify almost the complete opposite choice. This is a great illustration of how the simplicity argument becomes a tool to justify almost any choice.
I’ve argued that “simplicity” was an excuse to bypass some basic methodical thinking that could have saved Twitter the painful trial-and-error process. For example, it was a well known fact that Ruby didn’t provide a good concurrency story. In addition, Ruby was lacking many of the performance tuning measurement tools that exist in Java. It shouldn’t be that hard to realize that if your looking for scalability, relying completely on Ruby is going to put some strong limits into your architecture. If you add this to the mix, the question arises: is Ruby really that “simple”? Avdi brings another interesting observation on that regard:
Ruby on Rails made web development simple, relative to tools that came before it. It accomplished this feat by embracing "convention over configuration". In order to do this, it had to incorporate a great deal of extra complexity in the form of algorithms which guess the intent of the programmer instead of forcing her to specify her wishes explicitly. Anyone using it eventually has to become familiar with these hidden rules in order to understand why Rails behaves the way it does.
In other words it is “simple” if your application falls directly under the realm of assumptions of those who designed that framework and could turn out to be complex if you fall outside the boundaries of that realm. So the answer is that you shouldn’t take it for granted that ROR is “simple”.
Another example I’d like to use in this context is GigaSpaces. With GigaSpaces we worked really hard to make scalability simple. Our mission was to enable application to run on a distributed cluster of machines as if it was a single machine. We achieved that by removing the number of moving parts (SBA) and by providing an end-to-end architecture that will solve the scalability of the entire application from the load-balancer to the database. Yet the consistent feedback I heard, at the time we first introduced this concept, was that GigaSpaces is complex. When I asked users for an explanation the answer was quite interesting. Most users defined their problem domain in terms of a database bottleneck. They didn’t define their problem in terms of end-to-end scalability, and they often weren’t even aware that solving only the database bottleneck is not going to make their entire application scalable.
When cloud computing emerged, it forced users to think about elasticity. Elasticity means that you expect that your entire application will scale up or down when a new machine joins or leave the network. Elasticity forced users to look at how they can scale their entire application. All of a sudden, GigaSpaces appeared to be a significantly simpler solution then most of the available alternatives, which relied on static application server clusters and tier-based implementations that didn’t quite fit the dynamic cloud model.
That brings us to another question: What makes the same product “complex” at one point in time and “simple” at another point of time?
Avdi provides an interesting answer to that question:
Complexity is like an air pocket trapped under a layer of airtight plastic. If you push it down it just pops up somewhere else. Most of our attempts to "simplify" software really amount to pushing the complexity somewhere (or some-when) else. And our disagreements about what is meant by simplicity are really about where we think the complexity belongs. ..
In other words it all depends on where you define your area of complexity when you’re making your technology selections. Changing the definition of complexity from “solving database bottleneck”, to “application elasticity” yields very different results.
There are tons of examples I can think of where I could see how the same platform or product could turn out to be “simple” for a certain set of challenges and “complex” for other set of challenges. It’s important to note that even though I used Twitter and Ruby as illustrations, the arguments are not specific to those examples and can be applied to almost any language, framework or product be – be it PHP, Java, Spring or even GigaSpaces.
As Avdi pointed out, the goal of “simplicity” can be used to justify just about any decision and therefore we need to
be very cautious when we, or somebody else, bring it up to justify a choice. Making a certain complex problem “simpler” is often not enough, if your users define their complexity in a different area. I’ll close with Avdi’s advice:
I challenge myself and anyone who reads this, next time you use the word "simple", to stop and think about what you mean by it. As an exercise, try to rephrase it in terms of where you are pushing the complexity.