I used to have a personality quirk, and I still do to some degree, where I have to know exactly what all is going on around me to a great level of detail. In my house I could tell you exactly what is in every closet, drawer, or shelf in the entire house. Even movies that purposefully leave you in the dark can really frustrate me. I call this knowing my “state space”.
When my first kid was born this caused some issues. Whenever the baby went down at night I had to put everything away in exactly the right spot and in the correct way. It seems ridiculous now but I remember putting puzzles together before they went on the shelf. One night a puzzle was missing a piece. I spent maybe 20-30 minutes searching the house to find the piece to put the puzzle together before I could go to bed.
It’s certainly different now. I’ve learned to rely on systems and processes instead. There’s a designated “puzzle box”. I don’t worry about checking whether all the puzzles are there; I trust that’s where the puzzles belong and go. If a piece is missing it’s no big deal. If a piece is found then it goes into the box.
What does this have to do with coding?
In one of my jobs I was working with a fairly hefty chunk of legacy code. This was a large, complex computational analysis system. Inputs were fed in, a LOT of processing and analysis took place, and outputs and reports came out the other end. Very frequently there would be a defect in the outputs. We could do hand calcs and know that the report was wrong so we would begin the painstaking process of trying to find the bug in the code.
So here’s where my personality quick again manifested itself. I found that I had an innate desire to know the state space of the entire system and processing cycle. I needed to be able to walk the entire data and algorithm chain from start to finish. If there was a “black box” module with known inputs and outputs I couldn’t just wrap it in unit tests and let it go. I had to dig in and validate the internal algorithms. Another developer there, who had been there longer than me, showed me a different approach. He would zero in on a likely spot for the bug and if he found a single defect he would fix it and then rerun the analysis. Basically, he trusted the system and the process. I on the other hand would want to manually check the whole thing.
Eventually I figured out that his approach was the better one, and in this I learned a few valuable lessons.
Learn to trust the process.
The system had been running in production for a long time. It had produced hundreds and thousands of reports that were known to be correct. Fixing the one bug and moving on better aligns with agile, mvp, and CI/CD mindsets. Write code, fix bugs, trust your unit tests and QC processes, and move on.
(Of course, this means that you have to actually write unit tests. Make sure this becomes a habit with all of your coding, i.e. write the unit tests as part of the bugfix or new feature.)
Fix the problem in front of you.
I frequently see a lot of developers fall into a trap (including myself) where you fix the bug in front of you but then get caught up in reviewing and wanting to refactor everything around it. This is how a two-hour sprint issue estimate blows up into 9 hours. Fix the issue in front of you and move on.
This can be hard to do. Good developers are tinkers, love fixing things, and frequently fall in love with their own code and want to continuously improve things. While not a bad trait in and of itself, you do have to find balance with forward progress and alignment with business goals. If there’s 6 bugs that need to be fixed because there’s customer impact then you need to just get those bugs fixed as fast as possible and not get caught up in a bunch of refactoring just for the sake of refactoring. I’m not saying that reducing technical debt isn’t important but there’s definitely business value to getting those bugs fixed faster for your customer’s (and thus your revenue stream’s) sake!
Set aside intentional time for refactoring and reducing technical debt.
If you read above it sounds like I’m advocating cowboy coding, full-speed-ahead, ignore technical debt. This is not the case, because as any developer will tell you technical debt will crush you in the long run. But how can you handle technical debt while still making forward progress in terms of features? A good way I’ve found to balance this is to take every couple of sprints and create “hardening sprints”. Don’t schedule any features or bugfixes in that sprint (unless critical of course). Give the developers a week or two to go do all the tech debt reduction and refactoring to their heart’s content.
Modern software development tools and techniques give you an enormous, powerful toolbox to help you move so much faster, but you have to exercise good discipline and actually follow the processes. But if you do so, in the long run you can produce much higher quality code and move your projects along so much faster.