Wednesday, October 10, 2012

Rewriting and Forgetting

I once rewrote an entire program in another language. Why, you ask? At the time, I thought the software would improve by virtue of be written in what I considered to be the better language choice. Perhaps my naive assumption stemmed from the hope that customers actually care about such decisions when interacting with my software. I never had the opportunity to become that customer before realizing that hardly anyone using a system ever stops to think about the design decisions that have made their way in. The only ones who truly pause and think "hey, wouldn't this be spectacular if we could just rewrite this in a language that runs on many platforms instead of maintaining many code paths" are us — the ones who write the software to begin with.

This isn't to say that the end user doesn't hold any persuasion when it comes to throwing out entire software components and building from the ground up. Trying to operate a system that doesn't do what it says it's supposed to or the experience is just too appalling — that's good justification for a rewrite. Well, not an invitation to pronounce that the current code base is dead because there are some major problems customers are experiencing. That's how we forget. If we're too eager to dive into the code and start ripping apart the old and crusty scaffolding that doesn't look strong enough support a person let alone a car, than we're missing the point. We're taking the wrong cue from the customer and pretending that we know exactly what the problem is. But that often isn't the case. What the customer is generally saying when they complain about your code, is that I need some attention. I need you to consider what I'm going through when you design your workflows. And for that matter, I need you to consider the many different ways that I might go ahead and break your system. Not because I'm a malevolent person, but because I'm human.

In this inevitable scenario — a system that we've been developing for years without issue — we're dealing with the outside world. The outside world changes. Maybe it's your existing customer taking on new roles and pushing your software in ways that it wasn't designed to handle, or maybe you're taking on new customers who have their own ideas of just what the software is capable of. In either case, you're now faced with the task of replicating these issues, going through the code base with a fine-toothed comb to look for solutions that might resolve the dilemma you're facing with production deployments. However, and this has been the common case with myself, I'm happy to glace at the code and see that there are no real easy solutions. I can easily spin a story, in great detail, that articulates all the cons to carrying out any fixes to the current code. Reluctantly, my stakeholders agree. Rewrite the thing.

What's scary about this pattern of software evolution is that more often than not, we've got a list of backlog items that are classified under "wouldn't it be nice if we could" or "that method takes up three pages in my editor, I should do something about that". The good news is that we can now justify all these little architectural niceties that wouldn't have otherwise made it into the product. To this day, I like to keep my own notes on things we could do differently — code-wise — but don't simply due to the fact that it would mean rewriting much code and putting us in a risky situation. And I try to keep those notes out of reach, because they're opportunists, these little ideas. They like when we need to rewrite something due to customer demand and will piggyback their way into the code.

Again, this leads to forgetting. Why are we performing this rewrite in the first place? There has to be a really good reason to rewrite a large component let alone the entire system because it takes time. Rewriting a piece of a system doesn't happen over night, and during this time, you're development effort is closed for renovations. When you've got existing pieces of code that have been around for a while, you know how they work, Ryan knows how they work, they've been tested, and so on — you're not putting additional stress on your development effort. By this I'm of course referring to the little grab-bag of architectural ideas that we've all got close by. Of course, these will all lead to a more perfect world some day, but that's not what your customer cares about. You could fix twice as many problems the customer was actually facing in their day-to-day, but unless you nail those exact problems, you haven't fixed the software. At least not according to your number one stakeholder. So you've rewritten, produced better software, but you've forgotten why.

You might even think you're addressing all stakeholder concerns — your customer gave you a list, and you're on it. But it's that grab-bag again — maybe I can fix some of my customer's issues while at the same time address some underlying issues that I know will come back to bite us. Distraction once again from the real issue. With this mindset, it's as though we're hoping that by making improvements we deem important, some issues that customers deem important will go away. Or, at the very least, they'll certainly make fixing them easier. When a user of a software system has discovered something that prevents them from accomplishing their goal, maybe the better approach is to look at it the other way around. Maybe you need to traceback through the initial requirements that led the customer to believe that they could accomplish that particular task in the first place. Was that in fact something that your software was supposed to do? Maybe it does it, just sub-optimally. Maybe it's just the opinion of one user and is more trouble to address than its worth. But the key benefit to walking backward through the customer's experience, through the code, and back to the initial requirements, you can probably identify some interesting things. For instance, how could it be that a flagship capability isn't making the standards of the majority of our users? Is it a problem if our code quality, a problem with how we've captured our requirements?

The goal, when rewriting software isn't to make the improvements you the developer believe are the best changes as your customers may in fact disagree with you. Too much of that, and your changes that you've dreamed of for so long suddenly don't matter because your product is irrelevant. Instead, use rewrite sessions as an opportunity to understand what led to the situation in the first place. You should never have to rewrite entire systems — but you're going to have to rewrite a few that you get horribly wrong. Just be sure to identify what went wrong and remember that.