Lately I have been talking about clean application development, and how developers can do a better job of it. Due to the strong focus on this I decided to write some content from my latest talks into a series of blog posts. Enabling everyone to reap the benefits of the subject matter, and allow attendees of my talks to refresh the content covered at one of the events I spoke at.
In part 1 of this series we discussed reasons for creating dirty code, and the things that dirty code can cause. We followed with part 2 by giving a possible scenario of how developers get caught in a situation and start writing dirty code. Then, in part 3 we covered “smells” that hint to dirty code, and some possible situations that can be a bad “smell” highlighting potential problems. So, what happens when our code carries many of the bad situation covered in the first 3 parts?
Code that made us proud at one time has become an embarrassment we try to hide, and hide from. In part 2 the developer was very happy with his work in the beginning, and so was his boss. They were able to complete requests quickly, and everyone was happy to have such fast turn-around on new features. Then, little by little the problems of the application started to grow. The bugs began popping out, a change in one place leads to multiple problems in other areas, and time estimates start growing to compensate as the developers are forced to pad them for the unknown. As things become worse the developers tend to care less and less about the code, and this lack of caring shows in newly created code as problems continue to mount.
Many times the first signs of surrender come when the boss asks if hiring more developers will help get things done faster. It’s an honest question, and makes perfect sense on paper. If it takes two or three developers six months to create a new feature surely twice as many could do it in half the time. So the boss instructs us to start a search for more developers to hire, and we spend two or three months (or more) of our “spare” time searching, evaluating, and interviewing candidates until we finally manage to find a couple. After all, more resources equal more output…right?
Needless to say we go through a few developers, because all of them are not able to grasp the mess we’ve created. Others just leave for greener pastures rather than try to figure things out. Then a couple, either due to desperation or being hard workers, manage to stick around and get to work. But for some strange reason the time-lines never seem to improve, and it still takes a long time to introduce new features.
The boss is very confused, and not happy, because he thought more resources would allow development to get done faster. He even went to his boss and asked for permission to augment the team, and fought a good fight to get his way. Meanwhile the developers, seeing the writing on the wall, start scurrying to provide answers for failing to meet expectation. Technical debt has caught up to them, and it’s time to pay it off.
We talked briefly in part 1 about “technical debt”, and explained it was similar to buying things on credit. Using credit to buy things means the debt continues to grow and grow, until we’re forced to deal with it. Either we pay it off, or we go bankrupt and start fresh. In relation to coding, if we continue to develop poorly taking the “short way”, our technical debt continues to climb with each bad thing we do. Eventually we find ourselves in a situation where we either repair the code through refactoring, or we rewrite the application entirely and start fresh.
Unfortunately the first reaction to a poorly written application is usually a resounding “rewrite”. Developers feel like a weight is lifted from their shoulders, as the manager hears a distant “cha-ching” sounds of money slipping away. The old application took months, maybe even years, to build and cost a HUGE amount of money. Developers, hosting, tools, sales, customer support, and the hidden costs of insurance/rent/utilities/furniture to keep it all running add up. In the managers mind a rewrite means it must all be duplicated, and that’s not far from the truth when we consider the salaries of an entire development team for the time it will take to rewrite the whole application.
In the past there has been times when I inherited an application full of dirty code and recommended a rewrite to the client. On the surface it seemed like a good idea, and a win/win situation for both me and the client. It allowed me to make a little more money, because a rewrite take longer than simply doing updates or creating some new functionality. It also meant I could create the application “my way”. Because if it was built using my favorite framework, with my coding standards, and unit tests, it would be so much easier for me to work with.
The customer would benefit from a “better” application, pay for less time on future enhancements because it was “better”, and might even get some performance gains with less bugs from the application. The downside of course is that it costs the customer a much larger sum of money, and much longer time period, to have a rewrite done.
After much debate the developers may eventually win, and the manager will allow the rewrite. But only if the team is divided so part will be dedicated to building the new vision while the rest will support the old application. It only makes sense the developers who have been there the longest should develop the new application, since they understand the business better. (Even though they were the ones who created most of the bad code to start with.) The newly split up developer teams start working.
As the “experienced” team starts the new application the other team continues to fix bugs, and develops new features in the old application. It happens this way because business must continue to make money and service customers who continually need new functionality, or they will leave. Which causes endless scope creep for the team developing the new application. Not only are they attempting to build a new application, but they must also keep pace with new features added to the old application as well as incorporate any bug fixes and/or changes to the business logic. We start to see how difficult the task of rewriting becomes.
Rewriting started with the best intentions as everything is planned ahead of time, code written in an organized way, and perhaps unit test are even written. But as time moves on, and the scope continues to creep, the business side of the company starts to become impatient. The developers start to hear, “Is it done yet?”, and under mounting pressure to speed things up they fall back into the bad practices used in the old application. Before long the new application starts to suffer the same technical debt issues, and the downward spiral drags on. Ultimately the developers end up in the same position of apologizing for padded time-lines, unpredictable bugs, and cascading errors.
Usually by the time this entire process plays out there has been a change of developers, and maybe none of the originals are still around. The current team is now faced with two similar applications that both contain dirty code full of smells, and may not even have tests. They are now faced with the same choices of the previous team…to rewrite or refactor. And if they refactor, which application do they refactor?
Meanwhile the company, waiting for the new application, has been dragging its feet on some new features. Or perhaps have been promising the new application to their customers and building expectation. This costs the company money in lost revenue from lack of features, lost customers who went elsewhere to get the new features, and higher costs for the extra developers required for a dual-application path. Eventually, if left unmonitored, companies cannot recover and go out of business from scenarios like this.
On the other hand, what if we fought the urge to push a rewrite? If the current application is working and already contains all the business logic needed, but has a very dirty code base, why should the customer endure the loss of time and money for a rewrite? Plus, if we are new to the application and don’t really know all of the business logic, why should we endure the headaches and heated meetings while we discover every little nuance of a new application.
Now don’t get me wrong. I realize there are some circumstances where a rewrite is absolutely necessary. Perhaps the framework the application uses has had a major upgrade, or perhaps the application business logic is undergoing a large overhaul, or maybe the code is just…that…bad! Yes, there are times a rewrite is inevitable, and simply MUST happen. What I am introducing here is the idea that many times when we do a rewrite it may have actually been better for the customer, and maybe us as well, if we had simply performed refactoring as we went.
What I have come to realize as a much better solution than the rewrite story above is to follow some of the steps, but take a different turn. What I mean is that I still split the team up, but rather than instructing one team to build a new application I dub them as the “refactor team”. Their job is to start refactoring the code one module at a time. First they write unit tests, if there weren’t any. Then they start performing scheduled refactors. Meanwhile the other team starts following the new coding standards and styles, and writing unit tests as they continue to do bug fixes and adding enhancements.
Of course these two teams need to be closely monitored, and must communicate very often to ensure they are not stepping on each other. IT IS VITAL that everything be managed carefully, and that the teams are communicating open and often. Did I mention that communication is important, and it must happen often?
Sure there will be times when commits clash, and code will need to be altered using pair programming. However, the cost of time and money will be greatly reduced from a full rewrite. Not to mention it will help bring the development team closer together in the process.
After the initial modules are refactored in this manner it may be beneficial to rotate some members around on each team. This will ensure that team members on both sides of the fence do not become bored, or fall into bad habits.
Remember, I am not saying one way is better than another in every circumstance. It’s really a judgement call on which method is used to deal with the technical debt of the application we are working on. However, I hope this post has helped highlight that a rewrite is not always the best solution. Sometimes the best approach is to plan a series of refactors to recover the application from the junk pile. We must carefully analyze the entire picture and determine if a rewrite is a “must”, or is it a “nice to have”. Then we can honestly create a full list of pros and cons to more fully gain the big picture, and use that list to sell what we believe to be the best solution.
It may very well save the company from being a “used to be”, and instead make it a “will be”.