You are underestimating the compounding awesomeness of continually fixing small broken things.— rands (@rands) September 4, 2018
One of the perks of working remotely is being able to go on a walk around my neighborhood everyday at lunch. I'll often pick up bits and pieces of trash as I go, but there's this grouping of 3 houses I sometimes pass by where litter just seems to congregate.
It bothers me to see all that trash in front of those homes. To my shame, I rarely pick any of it up. Why bother? It would barely put a dent into the mess to pick up a few pieces, and it would be just as messy given a few days.
At least that's what I tell myself.
Those homes didn't always have all that litter in front of them. No, at some point one of them stopped picking up the garbage that would fall into the yard and then next house did the same and so on and so forth.
You can see how easy it would be to say to yourself, "my neighbor doesn't care, so, why should I?"
People don't typically set out to have low standards for themselves or their yards, but when everyone around you has them it becomes much easier to justify not picking up the tr
There's a threshold where normal people will cross over and join a riot. At first, the rioters might be simply opportunists or the angriest people among us, but there somewhere along the as the crowds grow more and more ordinary people join.
Codebases can be similar
We start idealistically. We follow all the best practices, but as deadlines grow tighter those best practices get slowly but surely ignored.
Our threshold for what's acceptable is lowered.
No one wakes up wanting to write a big ball of mud, but it gets there little by little until one day it's become one of a legacy application that burns people out on software development.
Broken windows everywhere. No one seems bothered by the litter.
Why not shove that extra logic into the 1000 line function in the 10000 line class?
No one else has written tests in the 10 years this application has been around, why should I start now?
All the other variable names are cryptic nonsense so another
foobarbaz won't hurt anyone, right?
Surely there's a better way?
When we get broken window codebases one of the first things developers reach for is declaring code bankruptcy.
We favor the rewrite, in other words.
Better to pull a Michael and declare code bankruptcy so you can rewrite the whole thing in fashionable framework X.
We would love to be able to tell the business folks that we need to spend the next 6-12 months rewriting this whole thing from scratch.
That the whole codebase needs to be tossed into a dumpster and lit on fire.
If only it were so easy, but, alas, business requirements.
So, what do you do when you can't declare bankruptcy and start over? What if you can't burn the whole thing down and start with a clean slate?
Even when you are faced with a behemoth you shouldn't underestimate the compounding awesomeness of making small changes.
It's true, we love the rewrite. We love not being weighed down with decisions made by... not us.
We want the Great Leap Forward and we think that can only be gained by starting over, but that's simply not true.
In a complex system, the cumulative effect of a large number of small optimizations is externally indistinguishable from a radical leap.
A large codebase is a complex system. They can become so large that any single change or improvement doesn't seem to affect the overall health of the application.
We're not looking to make one small change, though, but 10000.
In 97 Things Every Programmer Should Know laid out the Boy Scout rule:
What if we followed a similar rule in our code: "Always check a module in cleaner than when you checked it out"? Regardless of who the original author was, what if we always made some effort, no matter how small, to improve the module? What would be the result? I think if we all followed that simple rule, we would see the end of the relentless deterioration of our software systems. Instead, our systems would gradually get better and better as they evolved. We would also see teams caring for the system as a whole, rather than just individuals caring for their small part.
What the heck does that have to do with the Scouts?
The Scouts have a rule for their campsites: "Always leave the campground cleaner than you found it."
The key words there are "cleaner than you found it." It can be easy to think, "well, I didn't actively make it worse." But if the codebase is growing larger without improvements than it is undoubtedly getting worse, more difficult to understand, and harder to work with.
We simply want to leave the code as better than before. No more and no less.
Uncle Bob goes on:
If we all checked-in our code a little cleaner than when we checked it out, the code simply could not rot. The cleanup doesn’t have to be something big. Change one variable name for the better, break up one function that’s a little too large, eliminate one small bit of duplication, clean up one composite if statement.
The Scout Rule dovetails well with the Japanese philosophy of Kaizen which simply means "change for better," but has come to encompass an entire philosophy of continuous improvement.
"Change for better." Doesn't sound so scary, does it? It also doesn't sound as powerful as it is.
This idea of kaizen was a driving force behind Japan's rise after its industrial base was destroyed in World War 2.
Japanese firms emphasized what came to be known as “lean production,” relentlessly looking to remove waste of all kinds from the production process, down to redesigning workspaces, so workers didn’t have to waste time twisting and turning to reach their tools. The result was that Japanese factories were more efficient and Japanese products were more reliable than American ones. In 1974, service calls for American-made color televisions were five times as common as for Japanese televisions. By 1979, it took American workers three times as long to assemble their sets.
Kaizen was embodied best by Toyota which developed a system of manufacturing called the Toyota Production System (TPS). Toyota spent decades eliminating waste, streamlining processes, and making continuous small improvements.
These aren't great leaps forward, but a million tiny leaps spread out over decades.
At first changes were imperceptible, but given the long time horizon Toyota is now the most valuable company in Japan and the largest car manufacturer on the planet known the world over for their reliability.
If we plotted out Toyota's rise on a graph and chose any two points that were close in proximity the differences in improvement wouldn't have been radical transformations. Given enough time, however, Keith's Law was proved to be true.
The Toyota Production System brought about a transformation so radical it's indistinguishable from one radical leap.
Who says you can't apply that same philosophy to that legacy codebase you dread?
We understand intuitively that a single workout isn't going to make us fit. If you're out of shape and you go to the gym once you don't anticipate leaving looking like a bodybuilder.
No, you understand that getting fit requires dozens of small decisions and daily choices.
Going to the gym every week even when the motivation isn't there, choosing to eat healthy food, foregoing dessert, parking a little bit further away to get extra exercise in, etc.
To harness the compounding awesomeness of continually fixing small broken things requires daily and weekly choices, too.
If we focus on making the code 1% better while it might not seem like anything is improving at first the gains will blow us away over time.
James Clear has this great chart which illustrates the power of this approach:
1% better every day over a year and you end up with a 37x payoff at the end.
The problem is that many codebases aren't getting better every day. Instead, they're getting worse little by little.
Entropy takes over.
No one starts a project hoping to be working in a big ball of mud at the end. No, we start off with the best of intentions, but our best intentions can easily slip away if we aren't vigilant.
We need to constantly be asking ourselves the question:
- What's the next small broken thing I can fix?
So, what are some practical steps you can take to improve your code?
Uncle Bob's advice around the Scout Rule is good. Basically:
- Add tests where none exist
- Improve variable names (improve your names, in general) by making them more explicit
- Extract a bit of hairy logic into a well-named function
- Eliminate duplication (DRY out your code)
- Delete outdated comments
- Remove unused code
- Add documentation where processes are unclear
I'll tack on the following suggestion Jose Romaniello offers, as well:
- It took me 30 minutes to understand what this function does. Can I rename it to something more declarative or expressive so that others can understand it quicker than I did?
- I wasn't able to run this project with the provided instructions. Can I write the instructions that I followed but are not documented in a README file?
- This code is more convoluted than it should be. Can I make it easier and clearer?
- This function doesn’t have enough tests. Can I add some tests?
Step 1: Do more of what already works
Don't focus on only the latest and greatest techniques or frameworks. Focus on what is already working.
Avoid the novel (for now) and focus on what's boring but what works.
Step 2: Avoid tiny losses
In other words, make fewer mistakes. How can you do that?
One practical way is to adopt a coding standard and automatic linter/formatter.
Remove the tiny losses which come from inconsistent coding styles or making the sorts of mistakes that a linter can catch.
Still struggling with this one? James Clear gives the following examples that might spark some thinking:
- Education: Avoid stupid mistakes, make fewer mental errors.
- Investing: Never lose money, limit your risk.
- Web Design: Remove the on-page elements that distract visitors.
- Exercise: Miss fewer workouts.
- Nutrition: Eat fewer unhealthy foods.
Step 3: Measure backward
We're not aiming to transform the codebase overnight, but if we continue to make small improvements here and there before we know it be it in a month or a year or more we will look back to see that the codebase was transformed nonetheless.
More examples from Clear:
- Weight Loss: Measure your calorie intake. Did you eat 3,500 calories per day last week? Focus on averaging 3,400 per day this week.
- Strength Training: Oh, you squatted 250 pounds for 5 sets of 5 reps last week? Give 255 pounds a try this week.
- Relationships: How many new people did you meet last week? Zero? Focus on introducing yourself to one new person this week.
- Entrepreneurship: You only landed two clients last week while your average is five? It sounds like you should be focused on making more sales calls this week.
And some for code:
- You refactored/improved one module of code last week? Do two this next week.
- You improved 5 variable names yesterday? Improve 7 tomorrow.
Whatever measurement is important to you look to improve on it tomorrow, next week, next month, and next year
So maybe after all of that, you're still discouraged about the possibility of improving the codebases you work on and in. They seem too big.
It's a Sisyphean task.
Anne Lamott in her fantastic book on writing, Bird by Bird, shares the following anecdote:
Thirty years ago my older brother, who was ten years old at the time, was trying to get a report on birds written that he’d had three months to write, which was due the next day. We were out at our family cabin in Bolinas, and he was at the kitchen table close to tears, surrounded by binder paper and pencils and unopened books on birds, immobilized by the hugeness of the task ahead. Then my father sat down beside him, put his arm around my brother’s shoulder, and said, “Bird by bird, buddy. Just take it bird by bird.”
It's kind of like the old joke about eating an elephant.
How do you eat an elephant? One bite at a time.
How do you transform a codebase? One line at a time.
So, the next you're overwhelmed with the state of your codebase just remember you don't have to transform it overnight.
Line by line, friends. Just take it line by line.