By leaving problems in place, we are guaranteeing that we will run into them again.
Many times on many projects I've changed direction. We discover a new technique, new tool, or somehow figure out a better way to do something. The next step is to start using it. But if using that new technique requires that you change a lot of code, when should you do it?
When should I fix this?
I'll tell you a decision we made on one project recently:
"Let's switch to this new tool as we make other changes. We'll eventually touch every area where the old tool is being used."
We merrily coded along expecting to improve our project over time.
But that anticipated eventuality never came. Yes, as we moved some code to use a new tool, other areas remained with the old approach.
We later had to take time away from other work to bite the bullet and make all the changes we should have made. We spent more time removing the old way for the new.
So when should we fix it?
Postponing changes and improvements can add unexpected costs later.
Right NOW we understand the problem. We have time to understand it AND its solution. In the future we'll have to figure that out again. New problems, new ideas, and new priorities will call for our attention.
Worse, if the team is different in the future, we may lose knowledge of the problem and its solution.
When we only move to a better solution as we touch different areas of code, we leave in the old one, of course. This will lead future developers to wonder why we use one dependency over another.
I've played the git blame
game more often than I'd like.
Why is this here and that there? When did this change happen? Who committed this and what does the message say?
Researching and removing this confusion is development time wasted.
Although git commit spelunking can reveal good information, it requires purposeful commit messages. If you run into surprises, you'll need to spend time investigating commits. Sometimes laziness wins and commit messages are terse and uninformative.
Sometimes git commits don't have the answers.
Can't we document it?
If you can take time to document why code removal should occur, your time might be better spent removing it. If we add documentation for why a change has not been made, will it take as much time as making the desired change?
Let's add a deprecation warning
If you're building a library, a piece of an app shared by many, then deprecation might be appropriate. Upgrading to a newer version of a library brings complications. So a kind warning about future changes will help others prepare their own code for the change. Deprecations are great for users of libraries.
When we are writing our own app, we shouldn't deprecate code, we should remove it. We are the users of our own application code.
As James Shore put it on twitter "Do; or do not. There is no //TODO"
We don't have time. Let's skip the tests.
Sometimes adjusting our code means changing our tests. Depending on how you wrote your tests, that might be a painful process.
Most test runners provide you a way to skip or mark tests as pending. The messages you provide yourself and others here can be helpful for the future. Often that future doesn't come as quickly as we intend.
Yes, you can skip tests but that's not very different from deprecations and documentation. Your code still needs rework, and now your tests do too. Skipped tests leave noisy reminders in your test output. Inevitably you learn to ignore the noise.
How long will you ignore the pending test noise until you decide to finally address them. Skipped test are short term good with a long term loss.
Now what?
With problems in many locations, developers will wonder: "how are we going to dig ourselves out of this?"
We want to ship code and create features. Cleaning up often feels boring. There's no excitement in rearranging or fixing up our implementation.
There's no glory in this work.
Clean up feels unproductive because we're not adding a new feature. I have spent many project cycles feeling unproductive.
But I was actually helping my whole team move forward faster.
Without it we get stuck. Spending time finally moving to that library, or finally switching to that new method gives us freedom to focus.
And it's not just freedom to focus for me. Or... for you. It's everyone on your team. Cleanup work removes distractions. Finalizing decisions about what to do with our code removes unknowns.
Without cleanup work, the team loses time because these changes need to happen. Without cleanup work, the confusion becomes solidified. Your team multiplies the loss of time and focus created by indecisive implementations.
My "unproductive" project cycles spread improvements for the whole team. There's no glory in this work but, it is a part of shipping.
My project is different...
Maybe things are different for you. It's not likely but everyone likes to think that their project is really the unique one.
If you're not going to make changes that you should, ask yourself these questions:
- can this lead to system going down?
- what will developers do when they need to debug and fix it?
- how will other developers know which implementation/feature/library to use going forward?
- have I left informative commit messages?
Get advice from others
On two separate projects, my teams managed significant changes in two similar ways.
On one team we insisted that at least 3 developers discuss changes. That meant that if you discovered a new way to work, you got opinions and weighed the options looking for different perspectives. This helped us to spread knowledge about decision making. We were able to find alternative scenarios and ensure that developers played a part in decision making.
On another team with constant pair programming we chose fast meetings. We called a team meeting for everyone to discuss changes with significant impact. Our goal was to have the original pair determine options and explain to the rest of the team. When discussion we went beyond 5 minutes we put a stop to the meeting. We decided to reevaluate our work with new stories or deferred decsisons to the origin pair or team lead.
As a team we agreed to give one person or pair the authority of benevolent dictator. We would all support the decision and move forward. If we needed more discussion, we made plans for it and set the changes aside so we could finish up our work. The next step was to discuss the changes we wanted to make.
These interruptions allowed our teams to communicate about the needs of the project. But the interruptions were designed to be short.
Get advice from your code
You can look to your code and git commits to give you an idea of what areas of your code need your attention.
Use turbulence to check if you're likely to run into this code again.
If your code has a high amount of churn, you're likely to be editing it again. If it's got a high amount of complexity, you might find some bad habits in there that you could fix up.
Fix your code now
It's easy to find reasons to postpone making changes. It's even easier to skip it and leave a note in the code for later. In my experience, that time to read notes and make changes rarely comes.
Avoiding manual changes is often overvalued. Sometimes the best thing to do is dig in, find all the places that need updating, and make the changes.