Over the past dozen years, I’ve been involved in half a dozen engagements where my role was to lead an effort to rewrite a particular application. The applications were each very different, but there were quite a few similarities. This article gives my top ten tips for how to successfully rewrite an application.
Before jumping in, you should ask yourself why are application rewrites so common? In many cases, the application is working (at least, mostly) and it costs money to take existing, working functionality and write it all over again.
The main reason I hear is maintainability. Applications become too costly to maintain when you can answer “yes” to the following:
- Is the application built on technology that requires outdated skills or tools?
- Is the only way to know the application’s capabilities to “try it and see” or to get under the hood and look at the code?
- Is there major concern that minor fixes may cause major breaks in unrelated parts of the application?
- Is the application resistant to scalability?
- Is it hard to add small enhancements, never mind making major improvements?
Any one of these reasons can be used to justify a rewrite, but when you answer “yes” to all of them, it should be an easy task to justify the costs associated with a rewrite.
Once you’ve successfully argued your case and gotten the funding to move forward, this is when the fear sets in. Looking at the existing application at a high level, you have a good idea of what you want to build, but how do you ensure you get the detailed requirements right? How are you going to be sure that this time you get it right? You will want to make sure you choose the right technology and that you don’t build an application that is too brittle and inflexible for future enhancements? What will be different this time that makes this implementation more maintainable?
These are all good questions, but relax; you have a very good chance of being successful. Since most applications being rewritten were initially designed a decade or more ago, you need to be reminded that a lot has changed in that time. Even though we thought we had all the answers back then, we’ve come a long way and learned a lot in that time. All areas of technology have improved. Databases can handle many more transactions; storage is cheaper; there are multi-tenant implementations and don’t forget the cloud. I prefer to write applications using Java, and a dozen years ago it was in its infancy where frameworks like Spring were non-existent.
Processes and methodologies have come a long way also. Waterfall process has been used to successfully implement many important applications, but a quicker, more agile way of developing applications is vital to success in today’s world. Iterative development took a large step in the right direction, but now the Agile Development Methodology has made teams more productive and projects more likely to succeed (or fail) fast. While object-oriented development has been around for decades, the promises for reuse and maintainability are currently being proven every day.
So, once you conquer your fears and are ready to jump in, here are my top ten tips for things to remember as you consider rewriting your applications:
1. Design for Reusability / Be Object-Oriented
Hopefully by now all software development organizations know how to write good object-oriented code. For your application to be object-oriented means that you write classes of code that model your real world business domain. Your design leads must have a talent for recognizing where responsibilities lie and what one place in your system they should be coded. Avoid having multiple copies of the same code in multiple places as this becomes one of the huge maintenance pot-holes of the future.
2. Use a Layered Architecture
Which User Interface framework are you using? Which Persistence framework are you using? While these decisions are important, don’t tie yourself down. Layer your application so that you isolate the business domain, data persistence, services/business rules, and user interface. This layering will allow you to more easily change out a layer if necessary, while also enabling reuse and integration by other applications.
3. Choose your technology wisely
For some organizations, this is an easy step because technology choices are made by overarching organizations. For others, however, this becomes one of the more likely areas to stall. You can easily spend months reading about, evaluating, and hopefully prototyping different frameworks and tools. And just when you are about to commit, someone tells you a horror story about the one you’ve chosen. My advice is not to choose the latest and greatest of everything. Go with proven technologies that are still growing and evolving and especially look at what skills developers are investing their time in. Not all teams can ramp up quickly enough to implement a new set of frameworks on their own. If you have to hire or contract for additional skills, make sure the technology is one that allows your team to grow into it.
4. Find a leader that has done this before
Existing teams often have strong technical leaders that have been entrenched in the existing implementation and technology and who hold the keys to that embedded knowledge about the application. These leaders will be very important for the rewrite, but you also need someone who knows how to lead a rewrite effort. Too many times the existing resources are convinced that it will be too hard to rewrite the application and you will need someone with a fresh perspective who knows how to take your 4-ton legacy application and turn it into a svelte 500 pound dynamo.
5. Be Agile / Keep teams small
I am a huge fan of the Agile Development Methodology, not because it is the process of the day, but because it lets everyone on the team, including managers, developers, testers and technical writers focus on what gets the job done right.
One term you will hear associated with Agile Development is “fail fast.” This doesn’t mean you have less chance for success with Agile, it means that if you have made a choice that is not going to work for you, you will find this out sooner rather than later. Also, Agile is scalable to your team needs. If this rewrite will take 50 people to achieve your goals, organize into multiple smaller teams that can stay focused and spend less time trying to keep one large team on the same page.
6. Get Started Early
This tip does not mean jump in without in planning, but it does mean don’t wait until you have every single one of the answers before making progress. Many organizations want to know how long it will take to complete the rewrite that, by the way, is going to use brand new technology with a team that has never worked together before to implement requirements that aren’t well documented. Yes, these types of estimates are needed for getting funding and for acquiring resources, but you won’t really be able to validate that these estimates are close until you get started.
Early in the process, even before you know exactly which technologies you will be using or what design approach to take, form teams and start developing prototypes. Sometimes the word “prototype” implies throw away. In my experience, more times than not, the prototype becomes the base for the rewrite. Some of the code developed during this time is thrown away but that is a good thing. It is better to find out something was a bad idea while early in the cycle with a small team than later on – when it’s really hard to steer in a new direction.
7. Embrace Refactoring
Hopefully, you’re not still measuring productivity based on lines of code. There have been many occasions where the most productive days were spent eliminating lines of code. I have found that it is much more productive to write code early, before all issues have been flushed out, and then refactor once new information arrives. This is especially true when defining an inheritance hierarchy. If you have only one subclass, but know that others are coming, don’t subclass yet. Wait until the other subclasses surface and then refactor. One of the worst pitfalls for rewrites is the fear of refactoring. No one can get everything right on the first pass and trying to do so will be more expensive than doing it the best way you can now and refactoring later.
8. Automate Testing
Actually, you cannot embrace refactoring without automating testing. The first time you code a part of the system, you should not call it done until your automated tests are also complete. If you are using 2-week sprints for your development, and 7 sprints in you decide to refactor functionality that was completed in sprint 3, you need some way to make sure all the previously working functionality still works. Automated tests provide you the confidence that your code is not fragile. If a later piece of functionality changes something that was already working, your test should show it.
Beware, however, that the automated tests are code too. These must also be maintained. Choosing the right set of tests is important. My advice is that all code at all levels needs non-trivial unit and integration tests, and that automated test scripts for testing the user interface should be minimized. UI tests are the most expensive to develop and maintain and if your unit tests below the UI are done correctly, your UI tests are mostly testing navigation and interaction. The bottom line is that automated tests to take time and effort but this cost will return some of the value on your project.
9. Focus on Maintainability
This is probably the topic that got you here. Your new system must be maintainable, extensible and not fragile. All of the tips above have a maintainability aspect to them so I won’t repeat them here. The one additional point I will make is that developers should rarely, if ever, be clever. While there are many very smart developers who will take pride in writing code in a way that saves a 1000 lines of code or 15 classes, this code is rarely ever understood by those who have to maintain it. Encourage your developers to follow established patterns, reuse code and frameworks, and to always write clear, understandable code.
10. Be Practical / Don’t plan too much for the future
My last tip is the one that I rely on the most. Be practical. Too many times, in an effort to avoid future enhancement problems, designers of the new system try to plan for everything. Even though you might need a someday support mobile devices or live in the cloud, if you have no current requirements, don’t introduce complexity into your application for it now. Good architects and designers can provide guidance on what to avoid that will make implementation of these technologies in the future, but don’t write code for anything you don’t already have requirements for. If you use a layered design, write really good object-oriented classes, incorporate automated testing, and embrace refactoring, you will be able to incorporate future requirements without a major overhaul.
Hopefully, for many of you, these tips seem like common sense and are behaviors that you’ve been employing for years. For those who have been locked away in your legacy towers for years and are afraid of coming down to the current world of technology; never fear. Application rewrites can be a huge success and can make moving forward into the future a pleasure rather than a burden.
— Keith Shakib, firstname.lastname@example.org