Humans are interesting creatures. We are so much alike, but again so very different. Every single individual has its own way of perceiving things around them. For instance, I'm surprised by how often my wife and I have a completely different approach to the same problem. And that’s OK. We all have our own ways of handling things, methods that are based on numerous factors like experience, expectations, skills, different personal characteristics and principles, the list goes on. It's from this pool of diversity that the greatest creative ideas spring from, explaining why is someone a better fit as a doctor, someone else as a scientist or a pilot or a writer.
If we dig deeper into this topic, we'll notice that diversity also reflects individuality at a finer level. Software engineering is not an exception; These personal differences are the reason why we have different types of software developers: those who are into hardcore coding, those who are leaning more towards the UI design, or those who are born leaders.
If we dig even deeper and focus on coding only, we will notice the same effect. People bring inside their unique styles and way of thinking. But, besides the “human component", there is also one relevant fact regarding the coding itself.
Coding solutions can be implemented in numerous different ways. Every single project, every implementation, any logic or block of code could be written/rewritten in so many different ways it would be impossible to count. A newly created class is like an empty sheet of a paper. You can decide not only what to do with it, but also how. There is so much freedom, so much space for creativity. And what do we get when we combine such an open playground with different individualities and styles mentioned above? One word. Complexity.
Complexity is the worst enemy of every sophisticated system. Sounds like a quote, I know :) I just made that up, but we all know it’s true. Can you imagine how difficult it is to maintain a system which contains many complex parts and is continuously changing, updating and growing by adding new parts every single day? All those components that depend on each other, which were implemented by different people in different ways - are doomed to fail when something goes wrong.
“There are two ways of constructing a software design. One way is to make it so simple that there are obviously no deficiencies. And the other way is to make it so complicated that there are no obvious deficiencies. The first method is far more difficult.”
– Tony Hoare
Attempting to keep this maintenance difficulty under control is the main struggle of every software development team. Is there a way to make this struggle easier? Yes, there is, but we have to return to the “how” part. Let me copy/paste my own sentences: "A newly created class is like an empty sheet of a paper. You can decide not only what to do with it, but also how." How is the keyword here. So many discussions have been made because of how. So many coding guidelines created, books written, concepts refined, and principles formed.
KISS is one of those principles.
Before focusing on what it means for us, let me first "steal" what’s written on Wikipedia:
"KISS is an acronym for "Keep it simple, stupid" as a design principle noted by the U.S. Navy in 1960. The KISS principle states that most systems work best if they are kept simple rather than made complicated; therefore simplicity should be a key goal in design and unnecessary complexity should be avoided. Variations on the phrase include "Keep it Simple, Silly", "keep it short and simple", "keep it simple and straightforward" and "keep it small and simple."
Apparently, the term was coined by an engineer Kelly Johnson. At the time, he was the lead engineer at the Lockheed Skunk Works (creators of the U-2 and SR-71 Blackbird spy planes, besides others). It is known that Johnson handed only a couple of tools to his team of designers. He wanted them to design the aircraft by having in mind it should be repairable by an average mechanic under combat conditions with only these few tools.
I have to admit it sounds pretty cool to me, even challenging, to try to design a project architecture in a way that an average developer would be capable of maintaining it under difficult circumstances. If those difficult circumstances in the typical software development environment would imply unclear tasks, tight deadlines, continuously changing requirements, etc., it is pretty clear that the code implementation should really be as simple as possible. Do we ever write code by having that in mind?
Here are the main hypotheses behind the KISS concept:
- simple solution is better than a complex one, even if the solution looks stupid
- simpler solutions are easily understandable
- simpler solutions are faster to implement
- simpler solutions reduce implementation faults
- simpler solutions reduce testing effort
- simpler solutions are easier to maintain
- simpler solutions produce more reliable products
So, what does this actually mean for the software development?
Modern programming languages and frameworks usually offer different means of support for the creation of various sophisticated solutions. Sometimes developers feel the temptation to use that in order to write solutions which are more complex than they should be, just because the solutions appear:
- more clever,
- more appealing,
- more “cool”.
But, remember, “more is more complex”.
We often think we don’t introduce complexity. Focusing on the problem at hand usually leaves us blind to what our solution means for the whole project and for the other people inside the team. Sometimes we easily introduce new dependencies and classes on top of other classes and various helper code which “helps us” to code, etc., without stopping to think whether that’s the right way to go. This kind of approach can make it difficult for the newcomers to realize how things work or how to fix or modify something. How many times did it happen for us that we opened some of our old apps and realized how unclear the code is? Wouldn’t it be great to open a two year old project and be pleasantly surprised how self-explanatory the code is?
“Any fool can write code that a computer can understand. Good programmers write code that humans can understand.”
- Martin Fowler
We tend to complicate things more than it’s actually needed. Just check the beginning of this text. I started an IT article with “People are interesting creatures.”. I could have just started writing about the KISS principle, because that’s the whole point, that’s the actual topic, right? Instead, I introduced an unnecessary complexity in order to achieve the same outcome. Why did I do that? To try to make my article seem better, more clever? Whatever it is, we unfortunately do the same thing in software development.
“Life is really simple, but we insist on making it complicated.”
It’s in our nature. We often implement a solution to solve a problem by focusing on the implementation and the problem itself, instead of seeing the bigger picture. Taking the bigger picture into account would change the way we see the problem and definitely affect our implementation.
A friend of mine is using a custom layout keyboard. His layout takes into account which keys are statistically used more often and how can they be more easily reached with statistically most used fingers. His custom layout is not only better and more cool than the standard QWERTY, it’s actually also pretty smart and a very useful solution, but it has two major problems:
- no one else can use my friend’s keyboard
- my friend cannot use other people’s keyboards
Implementing a custom logic which helps you, but makes things more complicated for the others in the team, is a no-no. Always look at the bigger picture. Not only you have to ask yourself whether the others will be able to use your custom logic, but you also have to be careful you’re not becoming too dependent on it.
Why is it so hard to find a developer who can KISS well?
The almighty Internet says that software engineers need to be highly analytical, creative, persistent, innovative individuals who are also cooperative, responsible, adaptable and have a good attention to details. Sounds like a bunch of stuff, right? Regardless of whether all of that is true, we could say that software engineering can be very difficult, and cognitively demanding jobs often require people with high IQ. People with high IQ (if we believe the studies and if we believe the whole IQ concept makes sense) tend to overthink stuff. And overthinking in engineering equals overengineering.
“If our brains were simple enough for us to understand them, we'd be so simple that we couldn't.”
― Ian Stewart
Besides that, the very nature of software engineering job forces the engineer to be continuously surrounded with complex concepts which leads us to the professional deformation phenomenon.
“When the only tool you have is a hammer, every problem looks like a nail”
- Abraham H. Maslow
If we only have a certain mindset, we try to apply it to every situation even if that situation maybe calls for a different mindset.
There is a story floating around the space race in the 1960s. NASA scientists realized that ink pens could not function in space because there’s no gravity. They needed to find a way for the astronauts to write things down, so they spent years and millions of dollars to develop a perfect pen that could use the ink to write to paper without the gravity. Soviets, as the story goes, simply handed their astronauts ordinary pencils. We all know about this story and it's actually fake, but it’s perfect to make a point: If it’s getting complicated, you’re doing it wrong.
Complex problems can often be solved in a simpler way if we completely change the initial approach. I think that the out-of-the-box thinking should be listed as one of the engineer’s important skills mentioned above. The ability to come to the simplest answer with the fewest assumptions to choose between several potential solutions is a highly valuable skill and is usually referred to as “Ockhams’s Razor”. I think many devs out there can actually KISS well, they just need to put more focus on it, keep an open mind and be willing to always question their current mindset.
Is KISS always a good approach?
Some people are worried that KISS in practice could be quick and dirty. And that’s true. Simple approach to some people implies workarounds. I’ll paste a comment from one engineer who complained online:
"When I design a database I always try to normalize it. And I always had someone that said: “Ah, just put everything into a single table”. - And his argument was: "The design is just simpler with a single table. Think of KISS!" - And it just drives me nuts to always hear it."
Many solutions (even the bad ones) could always be justified with the KISS principle, and that’s why we need to be careful with it. The main key to maintaining simplicity is to recognize when complexity will be necessary.
It is important to realize that besides the essential complexity, there is also the accidental one. Essential complexity is the bare minimum of complexity needed to implement something. That’s the type of complexity which we cannot avoid, and which should be recognized by an experienced engineer. On the other side, accidental complexity is what makes implementation more complex than it should be. It’s the unnecessary complexity which is usually introduced while implementing the essential one, so the boundary between those two often isn’t that obvious. The key is to recognize that boundary and minimize the accidental complexity as much as possible.
It takes a lot of experience to kiss like a beginner
So, after reading this article we should all sit down and refactor our projects, remove all the dependencies, make everything simple, stupid? Of course not. KISS definitely shouldn’t be a rule of thumb in every case. Balance is the key. Currently, generally speaking, we are lacking in that. Software projects are much more often filled with complex concepts than with clear simplicity.
“Ninety percent of everything is crap.”
- Theodore Sturgeon, American SF author
I would personally be happy if developers (including myself) would question their logic more, and always keep in mind how easy their approach would be for the others to understand. Especially if by others we mean average developers or newcomers. We should all forget about our ego and put clarity and understandability first and always ask ourselves:
If something seems logical to you, stop to think if it will be the same for the others too? Does the custom code you introduced have any other value besides making your life easier? Software development is a team effort, not an individual thing. Always keep others in mind. If we would all be more aware of it in our everyday’s work, maybe the world (at least the software development world) would be a little bit nicer place and would maybe drop down by couple of places on those cognitively-demanding-job-lists :)
“Simplicity is the ultimate sophistication.”
- Leonardo da Vinci