Before we start
This is the start of a series about technical debt, and how to deal with it in your codebase. Before we jump in lets define what we are talking about.
What is technical debt? Technical debt is any accumulation of poor technical decisions, code that is buggy, brittle, or simply hard to maintain. It is even code that is difficult to test. Essentially technical debt is the trade-offs we make during development in order to meet deadlines, beat competitors, or simply because we are not aware of how to do it better.
It operates much like financial debt, being worthwhile at times to capitalize on opportunities, but if not managed will strangle a project with the interest that comes with it. In the long term, it causes changes to take longer, more turnbacks, and can lead to a difficult onboarding for new team members because they must understand archaic, often incomprehensible trade-offs made long ago. Left to accumulate for long enough it can eventually require you to file for "code bankruptcy" and rewrite significant portions of, if not entire codebases.
In this series I will walk through how to identify technical debt by recognizing clean code, some mitigation strategies, and even look at times when choosing to take it on is a wise choice. But first, let's focus on identification by understanding what good clean code looks like.
Unfortunately, if you were hoping for a quick, quotable description of what clean code is and how to achieve it, I think your going to be sorely disappointed. Clean code is a bit of a holy grail. It is a journey with only a fictional end to it. There is no such thing as perfectly clean code. It is nothing more than an oasis seen in the distance, a false hope for a goal that is impossible to reach.
What the heck!? you might say. What is the point if no code is clean? How can I make a difference if the goal isn't reachable? I visualize reaching a clean codebase a bit like a limit that approaches infinity. While you can't reach perfectly clean code, you can get very close.
Getting to a point where you can recognize clean code is, I think, a bit of an instinctual thing once you get the hang of it. And for me it was much easier to start with what wasn't clean code, and then get a feel for what was by working with the rest, and realizing the maintenance trade-offs.
So, what isn't clean code? I will start a list, but it is by no means comprehensive.
Clean code is not repeated.
Code that is repeated must be maintained separately. It must be tested separately. And worst of all encourages more of the same behavior by making it harder to do the right thing than just copy-paste once again. So if line after line are slight variations on the same logic, you are not looking at clean code.
Clean code is not overly clever.
My coworker and I have a litmus test for every design we come up with, and that is to check to see if it feels or looks particularly clever. Show it to another team member, how much do you have to explain it before they understand it? Overly clever code is not readable or understandable code. That is not to say that well thought out or unique solutions and designs are not permissible, just make sure you are keeping the cleverness at a reasonable level. If there is a less clever solution to your problem that still meets your requirements, and is more immediately understandable, then that is actually the better solution. Sometimes this means the performance is slightly worse, or it takes a few more lines to implement, but don't optimize before you have measured. Leaving archaeology level ancient maze puzzles for the next code maintainer is not a quality anyone wants in a teammate.
Clean code is not blindly following best practices
Best practices, industry standards, even principles like SOLID are fantastic guides that can help create quality code in a repeatable fashion. However, that does not mean they are overriding rules, nor that they should be followed in spite of the cost. Development is a constant set of trade-offs, and enforcing patterns, practices, or architectural designs blindly leads to worse code than you started with. If you see patterns forcibly followed even when the code doesn't fit or the pattern makes no sense, you are not looking at clean code.
Clean code is not all in one place
Ok, so we talked about what good code isn't, now let's take a moment to explore what good code is. Wait!, you might say, did you say you couldn't simply define what clean code was? Well I can't, but it does have a few pretty straightforward attributes that help identify it.
Clean code is easy to read
Readability leads to Maintainability. Maintainability is extremely critical for large code bases. Thus code that reads well is a key indicator of clean code. What makes readable code?
Code that uses naming appropriately - Properly named variables, properties, classes, and methods can take the readability from hieroglyphs to well written novel very quickly.
Code that stays at the same level of abstraction - Mixing logical or higher end concepts in with lower level string checks, math, or file access is jarring for the reader, and derails the understanding of the code. Clean code keeps nitty gritty low level details grouped together in methods, and separate from higher level concepts.
Code that utilizes white space well - Utilizing line breaks and spacing well can group logical constructs and provide hints to the reader about how to digest the code. Clean Code avoids running way off the screen with parameters, and has breaks for the reader to group lines into logical constructs.
Clean code is easy to change
Code is a living breathing thing that changes often. Clean code makes this easy by embracing that change within it's design. This may be by utilizing Dependency Injection, by properly utilizing interfaces at change points, or even simply by how the code is broken up into classes and assemblies. Regardless of how you get there, clean code makes sure that when that customer request, regulatory requirement, or new feature comes along that the codebase is ready and willing.
Is this everything that clean code is? No. But it should help you evaluate code and get the instinctive feeling for when code is clean, or when it is technical debt.
Now that we have a general idea of how to identify Technical debt, next time we will dive into mitigation strategies for the "unclean code".