A code repository is like a warehouse of assembled Lego models. Some of the models are connected and work together. Others are more standalone. The models may or may not have been put together well. Simiarly, it's possible to find the warehouse in a variety of states, some more desirable than others. In the ideal scenario, models are organized on the shelves and in the building in a way that makes logical sense. There are probably some exceptions to the organizational structure because code components don't always cleanly fit into one category. Code, like life, can be messy and difficult to keep optimally organized.
Imagine you are a Lego engineer and you're assigned the task of replacing the Darth Vader minifig in a Death Star set that has been built. Let's walk through the steps of performing the work in 2 different, theoretical warehouses.
The Clean, Organized Warehouse
You walk into the well-lit warehouse. The floors are gleaming, the lego models on the shelves are neatly organized, and there is a faint scent of cleaning products in the air. You scan the shelves: "Castle, Classic, City, Disney, Harry Potter, Star Wars, Technic..." The alphabetization pleases you and you can't help but begin to whistle the Imperial March.
You walk down the Star Wars aisle and notice that similarly themed sets are grouped together: Rebels, Empire, Battlefront, Mandalorian... You locate the Death Star set identified in the Jira ticket.
It doesn't take long to identify the minifig to be replaced. There are a few components of the Death Star that need to be removed to get to him, but the model was built well and it doesn't take long to disassemble the parts that are in the way. You remove them, construct the new Darth Vader, replace the old Darth Vader with the minifig you just put together, and reassemble the Death Star. The new minifig required a little more space so you moved some of the pieces to a slightly different location, but the finished change looks great and the work took less than an hour.
You take a moment to admire your work, and then stroll happily out of the warehouse, confident that this will reflect favorably in your end of year performance review.
The Messy, Disorganized Warehouse
You walk into the warehouse as the lights flicker ominously. Immediately you stumble into discarded boxes on the floor with a note attached: "TODO: clean this up". Out of the corner of your eye you're almost certain you see a rat scurry behind a shelving unit.
You scan the shelves: "Classic, Harry Potter, Miscellaneous, To Be Organized, Space Stuff, City?..." Ok, it must be in Space Stuff. There is a table blocking the Space Stuff aisle. On the table is a model that is connected to one of the sets on the shelves. It needs to be disassembled just to move the table out of the way. You look to see if you can get around the table without having to take things apart but, after some time, you conclude that you have no choice but to disassemble the sets to be able to walk down the aisle.
After having cleared a path for yourself, you make your way down the aisle. There are space-related sets here, for sure, but there are also city and castle sets here that clearly don't belong. You spend some time walking up and down the aisle but no Death Star.
Frustrated, you reassemble the set you initially disconnected on the table, and check the other aisles. After some more time, you discover the Death Star set in the Disney aisle. Ok, fine, maybe technically valid, but not the ideal location.
After moving aside another pile of boxes and trash, you examine the Death Star and your heart sinks. This is not put together well. Someone clearly slapped this together without much care or thought. And.. what is this? Some of the pieces are glued together! Why would someone glue them together?!
The components aren't as modular as the instructions intend them to be so you end up taking apart much more of the Death Star than should be required just to get to Darth Vader. It's also clear that not all pieces are part of the original set. Someone has incorporated parts from city and castle sets that don't quite fit right. You create a Jira ticket to replace the incorrect parts later.
It takes some time to pry loose the glued parts, and a few become damaged in the process, requiring replacement. The job takes all day but you're determined to leave the project in a better state than you found it. You replace Darth Vader, reassemble the Death Star, improving the build to the extent that time will allow, and consider the task complete.
You walk out of the warehouse mentally tired, but still with thoughts toward how to reorganize things to make future work run more smoothly.
Comparing the Warehouses
It's not a perfect analogy, but it illustrates the idea that changes can be made more easily when the previous work was performed well. Clean, thoughtful work is not just for the present, it lays the groundwork for better work performed in the future. You're not just making the change for the current team, but for the new engineers to be onboarded and for the engineer that got paged to fix a production issue at 2am. The cleaner the codebase, the quicker someone unfamiliar with the code can get up to speed, and code is read much more frequently than it is written.
What is Clean Code?
- Code that favors simplicity and readability
- Short functions
- No God classes
- Folder structures that make sense
- Simple, shallow class inheritance
- Modular, not repeated code
How do Code Repositories Get Messy?
- Lack of planning
- Lack of teamwork; engineers each developing different parts of the code their own way
- Inexperienced deveopers without senior mentors to review
- Short deadlines and pressure from management to get things done quickly
- Complacency
- "We can fix it later" mentality
It takes time and effort to plan things and implement features the right way. But you're going to pay that cost regardless. You either pay the cost up front to maintain a clean codebase, or pay it later in the form of future changes taking more time. In the latter case, the effect compounds: future changes take longer, so there is even more pressure to move faster, and engineers are moving frantically just to try to keep up. Ironically, trying to move quickly is often the source of work taking so long.
"We'll do it this way for now and then clean it up after the release." If that were true, then it might work, but in over two decades of software development I've rarely seen that happen. The feature requests and bugs don't stop and they almost always get prioritized over work to clean up tech debt: suboptimal work performed in the name of getting things done more quickly. The irony is that if the tech debt is never created to begin with, work naturally takes less time to complete.
What is the Solution?
I present these in order of priority and effectiveness:
-
Don't create tech debt to begin with. I know, I can't even type that without chuckling. This is the ideal solution but almost impossible to achieve. If you happen to find yourself on a team that never creates tech debt, cherish those people and do whatever you can to make that organization your home.
-
A manager that can regularly carve out time to address tech debt alongside the feature work or bug fixes that the business requires. Managers like this are rare and valuable.
-
Hold each other, as engineers, accountable. Take the time to review each other's work and tactfully and sensitively suggest changes. "Tactfully", of course, being the key word here.
-
On an individual level, clean things up as you see them. Make doing good work a personal value. I believe most engineers already feel this way to some extent. That being said, I think #4 alone is not sufficient to keep codebases clean (unless you're the only one working on it). It almost always requires one or more of the previous 3.
At a high level, applications and code tend to get increasingly complex. The sooner a team can establish good habits and practices, the better off everyone will be in the long term.