Introduction
It’s August, so I’d like to take a break from the technical deep dives and share my thoughts on broader programming topics, like the KISS principle.
In a previous post How To Choose Between Conflicting Programming Principles I mentioned that I deliberately avoided discussing the KISS principle, explaining, “I find KISS to be a poor principle and believe it should not be used as a guiding factor in architectural decisions.” In this post, I’ll explain why I hold that view.
The KISS Principle, which originated in engineering, has since permeated various disciplines, including programming and code architecture. While it may have its place in other fields, I believe KISS falls short when it comes to guiding code-related decisions.
Unlike other programming principles, which are typically well-defined and aimed at addressing specific problems, KISS is more ambiguous. As I’ve emphasized in previous posts, the real value of a programming principle lies not in the principle itself but in the specific problem it seeks to solve. The key is for a developer to identify the problem in their codebase, assess its potential impact, and then apply the appropriate principle, with a clear understanding of its benefits and drawbacks.
KISS aims to tackle the problem of complexity. However, this is where it may falter. What exactly is complexity? Is it objective, or does it depend on context? Can we reliably distinguish between what is complex and what is difficult, or between what is simple and what is easy? Even if complexity is contextual, will the same developer make consistent decisions in identical circumstances, or could their mood affect their judgment? And how do knowledge and experience influence perceptions of complexity and difficulty?
These questions, and others like them, reveal the limitations of the KISS principle as a tool for advocating better code. In the rest of my post, I’ll delve deeper into these issues, explaining in greater detail why I find KISS problematic and why I prefer to rely on other programming principles when writing code.
Two Usages Of KISS
I’ll begin by clarifying that my discussion of the KISS principle focuses on code that will be read and maintained by ourselves or other programmers in the future. I’m not referring to the end product—the program itself—which is used by end users. The complexity of a program’s usage is influenced by UI/UX design and decisions made by designers, not by the underlying code architecture. This post specifically addresses the use of KISS as a principle that impacts code architecture.
In this context, KISS can be invoked in two different scenarios: as a guideline for writing our own code or as a critique of code written by someone else. The latter is one of the most problematic ways to use KISS. This is because we should never compare real-world code to an idealized, imagined version of what that code “should” be.
Comparing Real Code With Imaginary Code
Imaginary code is code that exists only in our minds. We might look at a program and conclude that our imagined version would be simpler or better. The problem with this kind of thinking is that imagination is always more idealized than reality. Of course, something we imagine will seem simpler or better—it’s how imagination works. But in programming, this mindset can lead to serious mistakes in judgement.
Our minds are adept at dreaming up solutions, but they are flawed compilers. When we compare a real program to an imagined one, there are countless edge cases that our minds simply won’t consider. Imaginary code seems perfect until we try to implement it, only to find ourselves writing code that is just as complex—or even more so—than the code we initially criticized. This doesn’t mean we can’t evaluate or critique the architecture of code we didn’t write; it just means we should avoid comparing real-world code to an idealized version that only exists in our heads.
When reading code and questioning its architecture, if the critique is based on the notion that “it’s not simple enough” or “it could be simpler,” or worse, “I would have done it differently because I follow the KISS principle,” then this argument is flawed. Of course, something imagined will seem simpler. The only valid comparison is between two actual codebases, but writing code from scratch leaves us with only the first usage of KISS: As an argument for writing and architecting our own code.
Simple Is Subjective
Let’s begin with the obvious: simplicity is subjective. This is one of the key reasons I don’t consider KISS to be a strong programming principle for guiding coding decisions, though it’s certainly not the only one.
How simple something appears depends on factors like knowledge, experience, talent, and intelligence. This wouldn’t be a big deal if we were only writing code for ourselves, but in reality, the code we write will often be read and used by other programmers—either by our team or by developers who will maintain the codebase in the future. As our understanding of programming evolves with knowledge and experience, so does our perception of simplicity. Therefore, when writing code, we must consider not only what seems simple to us but also what will seem simple to our colleagues.
Another aspect of the subjectivity of simplicity relates to the specific goals we have for a codebase. Code that is simple to extend is different from code that is simple to understand, which in turn differs from code that is simple to test, debug, or allow multiple developers to work on in parallel.
Finally, simplicity is influenced by real-world constraints. Do we have time limitations? If so, the simplicity of an implementation often aligns with how quickly it can be written. Are we expecting future requirements that will necessitate extending the program’s functionality? In that case, a quick succession of ‘if’ statements may no longer be simple, implementing a strategy pattern might actually be the simpler approach in the long run. What about testability? Code designed with easy testing in mind may not seem simple when compared to prototype code, but if extensive testing will be necessary in the future, simplicity for testing may look very different from simplicity for functionality.
The subjectivity of simplicity is likely what most people think of when they hear that I don’t like KISS, but there are additional reasons I’ve identified over the years that have led me to avoid relying on KISS when making coding decisions.
Complicated Is Different From Hard
There’s an important distinction between what is complicated and what is hard. Complicated is the opposite of simple, while hard is the opposite of easy. These concepts can exist in any of four possible combinations. Generally, tasks that seem simple and easy are often those we’ve underestimated; once we start implementing them, we quickly realize our mistake. On the other hand, tasks that seem hard and complicated are usually those we haven’t fully thought through. By breaking them down into smaller pieces, we can often make them either less complicated or easier to manage. This leaves us with two combinations to consider: tasks that are hard but simple, and those that are complicated but easy. These aren’t binary states, but rather spectrums where a task may lean more towards one or the other. This distinction is applicable not only in programming but in many aspects of life.
For example, breaking rocks with an axe all day is hard but simple. Similarly, understanding how to implement an algorithm that checks if two binary trees are equal might be hard but simple. The benefit of tasks that are hard is that, with time, learning, and experience, they tend to become easier.
Conversely, sitting in an office all day managing a large team of people might be easy but complicated. The same can be said for creating multiple layers of abstraction in a program: it may be easy to understand what each layer does individually, but understanding how all the dependencies work together is complicated. Complicated tasks don’t necessarily become simpler over time; they remain complicated unless changes are made. Either the process needs to be restructured to simplify the dependencies between moving parts, or the complexity will likely increase over time, regardless of the knowledge and experience gained.
The difference between hard and complicated can be confusing, especially in areas where someone has limited knowledge and experience. This confusion can be problematic when decisions are made based on the KISS principle. Mistaking a task that is hard (and will eventually get easier) for something that is complicated (and thus trying to simplify it prematurely) can lead to laziness, a lack of knowledge, misinformation, and ultimately, becoming a less effective programmer. Below are some more detailed examples of these issues:
KISS Can Be An Excuse For Laziness
The KISS principle can sometimes serve as an excuse for laziness. For example, consider my post on What Are The Implied If Statements In Code. In brief, a programmer might write less code by relying on implied if statements that aren’t explicitly written anywhere, which can violate The Tell Don’t Ask Principle. For instance, if a float variable named ‘damage’ represents damage when positive and healing when negative, and the programmer claims this is simpler because it requires less code, it’s a clear example of using KISS not to simplify but to avoid writing more code.
The same applies to the decision to use multiple if-statements instead of implementing a strategy pattern. Initially,a few if-statements might seem sufficient, but then as our program scales to something bigger, we start adding more if-statements and nested if-statements. This happens because we’re too lazy to refactor, convincing ourselves that it’s simpler. Why spend time refactoring when we can implement the same functionality more quickly? Why create dependencies between classes when everything can be managed from one big class? However, these justifications quickly unravel when we realize that while we’re coding twice as fast, we’re debugging five times as slowly, or that although we’ve avoided dependencies between classes, we’ve created complex, hard-to-test dependencies through global variables that change state unpredictably.
Everyone gets lazy sometimes. When someone is tired and just wants to finish their work for the day, it’s easy to use KISS as an excuse for cutting corners.
KISS Can Be An Excuse For Not Learning New Things
The KISS principle can also serve as an excuse for avoiding the effort to learn new things. Learning something new is often challenging, and it’s more comfortable to stick with tools and techniques we already know. However, as I previously mentioned, there’s a difference between something being hard and something being complicated. Despite this, it’s easy to justify our reluctance to step out of our comfort zones by claiming it’s simpler to use what we’re already familiar with. Why bother learning a new library that everyone praises when you can achieve the same result with one you already know? Why explore a new language, framework, or tool when your current knowledge seems sufficient?
Even if we decide to try something new, the initial learning curve can be difficult and time-consuming. It’s tempting to avoid this discomfort by invoking KISS as a reason to stick with what we know. Unfortunately, this mindset can hinder our growth as programmers. Our willingness to embrace discomfort and dedicate time to learning new things directly influences how much we evolve and improve in our craft.
Using KISS as a time-saving excuse, or confusing “hard” with “complicated,” is akin to a five-year-old relying on their fingers to add numbers. A child might quickly and easily add small numbers using their fingers, making it seem unnecessary to invest effort in learning proper addition methods. Even if someone explains that finger-counting is impractical for large numbers, the child might argue that they’ll learn the proper way when they need it. Until then, they’ll keep it simple.
Obviously, anyone older than five knows that using fingers to add numbers isn’t the right approach; learning the proper method isn’t complicated, it’s just hard at first, but it becomes easier with practice. This isn’t about simple versus complicated, it’s about hard versus easy, but KISS can be misused as an excuse. In this analogy, that five-year-old could be any one of us, resisting the challenge of learning something new and hard, which ultimately wii become easier over time.
KISS Is Easy To Abuse
Finally, the KISS principle is an excuse that can be easily abused in various situations, primarily because “simple” is a term that’s hard to define and often used incorrectly.
When we’re under time constraints, it’s tempting to invoke KISS as a justification for writing code that merely works. This isn’t to say that creating functional code under a deadline is inherently bad, as long as we acknowledge that this code may need refactoring later. However, by leaning on KISS, we might overlook that need, convincing ourselves that the code is “simple” and thus good enough.
KISS can also be misused as a way to avoid communication. Effective communication, especially with those providing requirements, is a crucial part of every programmer’s job. But when requirements are unclear—and let’s be honest, many programmers aren’t naturally extroverted—it’s easy to use KISS as a reason to implement what we think is the simplest solution, rather than seeking clarification.
KISS Can Increase Productivity But Make You Worse Programmer
As a final reason why I don’t like the KISS principle, let me describe a situation that probably deserves a post on its own, but misusing the KISS principle the wrong way can add to its negative consequences. KISS can be used as an excuse for increased productivity. You might wonder, isn’t productivity the ultimate goal for every programmer? The answer is no.
The primary goal for every programmer should be to become a better programmer. Productivity should be a natural byproduct of that growth. Anything that makes writing code simpler and more productive but doesn’t contribute to improving our skills might benefit our employer, but it’s detrimental to us. Asking for solutions about problems with our code in forums without taking time to debug ourselves, copying answers with code from these forums without actually understanding it, copying code from sites like stack overflow without trying and solving the problems ourselves or without trying to understand why the code works, doing the same with any AI LLM, can be a lot simpler and certainly we will be more productive as it will be faster in the short term, but eventually will make us worse programmers. And while we are getting worse or staying at the same level, others will become so much better that their productivity eventually will be better than anything we do.
Consider a student that one day comes and says that he found an amazing way to be better at math. He has discovered a book with the solutions to the problems in his school’s math book, so he doesn’t have to try to solve them anymore. By copying these solutions, his productivity has increased ten times more. He can solve in 10 minutes what used to take two hours. But as time goes on, his peers who took the time to solve the problems themselves will have developed a much deeper understanding of math, leaving the shortcut-taker far behind.
Productivity should not be the end goal for any programmer, it should be the result of getting better in programming. KISS can be used as an excuse, because copying solutions is simple and less time consuming, but in the long run the result of using KISS like this, will be to stay behind in your career as your peers have become a lot better than you. After all, the experience they gotten in those years will be how to solve programming problems, the experience you will have gotten in those years, is how to copy answers that have limited application to more complicated problems and as the problems get harder, the less your chances will be to get the correct solution. While they have gained valuable experience solving complex problems, you are stuck with a superficial understanding, ill-equipped to tackle more challenging tasks as they arise.
Conclusion
This post ended up being longer than I expected, but that’s not surprising given how strongly I feel about the KISS principle. Beyond the subjectivity of what is considered “simple” and the confusion between “complicated” and “hard,” KISS can easily become an excuse for laziness, poor decisions, limiting our knowledge, unfairly judging other people’s code, and ultimately stunting our growth as programmers. Even if we’re aware of these issues, it’s easy to fall into the trap of using KISS as an excuse when faced with a difficult problem, tight deadline, or when we’re just exhausted in order to avoid uncomfortable or stressful situations.
Thank you for reading, and as always, if you have any questions or comments you can use the comments section, or contact me directly via the contact form or by email. Also, if you don’t want to miss any of the new blog posts, you can subscribe to my newsletter or the RSS feed.