If you’ve been programming for any length of time, you’ll likely run across apps or other projects that need major work and revisions to keep going. Maybe your company acquired another company with tons of legacy code. Perhaps you just started a new gig with lots of skeletons in the proverbial closet. Or you’re an indie developer and that old reliable app that has paid the bills is finally showing its age. I’m going to try to help you make the decisions about your apps (or really any code projects) that need this kind of work.
So it’s easy to get overwhelmed initially by the scope of changes needed or the amount of time required to fix everything. Where do you start? Do you just scrap the existing code and start over? Do you try to refactor the legacy code in place and modernize things without creating a new greenfield project? Only you can choose what’s right for your situation; therefore doing so with clear motivations and direction is key.
Before You Start
- Leave your emotions at the door – Whether you’re reviewing your own code, programming that you inherited from another dev, or something in between, it’s critical not to come at the problem too attached to (or the opposite, too ready to dump) what’s already there. Assuming that you have decided to make some significant change, making decisions will be easier looking at the code objectively.
- Try not to bring preconceived assumptions – While it’s perfectly fine to have a hunch as to which way to rewrite an app, often the discovery process of reviewing an existing piece of code will strongly guide the decision making process (as it should). Let process and analysis drive, rather than your gut, where you can. I’ve seen perfectly good, reusable code thrown out because some developer has carried the “this code sucks” chip on their shoulder entering an app review. It’s also easy to get swept up in new technology hype too early (or stick with older tech too long). While SwiftUI may be awesome, for example, it’s not the hammer that will pound every nail out there. It may turn out that reviewing the code flows the work in another direction.
- Understand your options – For what we are discussing here, your choices will range in scale from “do nothing” to “refactor in place” to “start a brand new project”. Regardless of the choice, it’s critical that you (and your team) understand your capabilities as well as your limitations. It’s all well and good if you want to ditch that old Objective-C code and spin a new SwiftUI app but if you haven’t learned SwiftUI or don’t fully understand how Objective-C works, your task will be much more difficult.
Understand the Decision Making Drivers
While there are numerous paths to software project analysis, I’ve found that at their core, planning most coding projects does come down to a combination of the familiar three interrelated factors:
- Resources – How much money and how many programmers, designers, etc. can be thrown at a solution? Resources can come in other forms as well (software, hardware, etc.).
- Time – When is the project due? What’s your deadline? Obviously important but keep in mind that there are absolute minimums in many cases. The old adage about not being able to make a baby in 1 month with 9 people applies here.
- Scope – What has to be included in the finished app or project? Where the choice is refactor or rewrite, it doesn’t have to be an absolute in many cases. You may want to refactor some of an app and totally scrap and redo other parts. Scope can be flexible.
Usually adjusting one or two of these options will force the unadjusted option to change in the other direction. For example, adding more programmers (resource) might mean you can deliver the same amount of work (scope) more quickly (time).
By assessing your options in this light, you should hopefully arrive at a deliverable , or set of deliverables that can be delivered over time (enabling a more Agile approach) that meet your goals. Making the kinds of decisions around reworking an entire app almost always involve some level of compromise on one or more of the above axes, so it’s important to be ready this up front.
As you assess the issues, try to remember these criteria and make sure that you are also weighing the benefits/drawbacks of any changes. Any business (including yourself if you’re on your own) should want to see positive benefits (financial or otherwise) from any expenditures of time or resources. Keep track of the pros and cons as you look at rewrite vs. refactor.
Analyze the Existing Code
Looking through someone else’s (or your own) legacy code may not seem like a good time, but you can learn a lot in the process. Keep an open mind and remember that there is almost always a reason for the code you are reviewing. Coders aren’t malevolent in general; we all however have knowledge gaps, operating pressures, and have to deal with the technologies available at the current time. The mobile app space especially has moved fantastically quickly since the first devices came out; and both Apple and Google continue to rapidly evolve both UI and non-UI paradigms for developers.
There are more different types of refactors and conversions than I can list. Here are some of the main ones that mobile app developers tend to hit most often, along with some related thoughts.
- Language Conversion – For example, Objective-C to Swift or Java to Kotlin. Extremely common these days and can be fairly straightforward in some cases but extremely complicated in others. Built-in system tools to convert Java to Kotlin or Xcode add-ons like Swiftify on the iOS side are good but generally only take you part of the way. You should expect significant clean up efforts both in terms of the code as well as being forced to move to different language paradigms (async/await anyone?). Use tools like Cloc to count lines of code by language and help gauge progress.
- UI Conversion – With the advent of SwiftUI and Jetpack Compose, it’s very common to want to take entire apps or portions of apps and modernize them. Older apps with nib files or storyboards are prime candidates for these types of refactors. Generally speaking UI conversion doesn’t have to be an all-or-nothing; so that’s probably the biggest thing to keep in mind. Depending on the application architecture as well, it may not be possible to update just the UI without additional refactoring needed to fit the new architecture.
- Architectural Refactor – All of the existing apps out there are written with one or more of many different architectures – good, bad, and ugly. Whatever your choice of preferred architecture, you may wind up having to deal with an app that is architected differently, poorly, or inconsistently. Changing an app architecture is usually a very significant step and can have many consequences. Really think about (and comprehend) the benefits and drawbacks of this type of change before jumping off the proverbial cliff.
- API Replacement – These types of changes can actually be some of the simplest – if you are able to just swap endpoints and leave it at that. Complications often arise though with situations like changing data models/contracts and moving from one type of API to another (e.g. REST API to GraphQL) which might require new network code or third-party SDK’s.
- Cross-Platform to Native (or vice versa) – These are probably less common but do happen as application needs evolve to be more complex or devolve and remove features. Business acquisition and available technology expertise can also play a role here. Flutter, Xamarin, React Native, etc. all have their own unique wrinkles. If you’re new to either cross-platform or native, it’s critical to do enough prep work, proofs of concept, etc. before making hard decisions to avoid any showstoppers or other failures.
That’s enough prep work and thinking. Now get in there and do the fun stuff!
Make the Call
So you’ve decided on your initial goals, reviewed countless files and lines of code, and think you’ve got a few options. Some choices are quick and easy, some aren’t. You think you may be able to get some resources to help but aren’t sure. What’s next?
If you haven’t already done so, get organized. Lay out the pros and cons for each option you identified – both financially and otherwise. Don’t be afraid of including intangibles like improved developer morale, easier maintenance in the future, etc. Try if possible to cast these things in light of how they will help (or hurt) the business that’s running the app.
Then, make a decision! What did you decide? Scrap and rework? Refactor in place? With significant efforts like these, it’s really easy to get analysis paralysis and get sidetracked until the effort itself becomes obsolete or, worse, becomes forced upon you by some outside force (like the dreaded Apple or Google policy update). It’s totally ok to abandon the effort to refactor or rewrite if it’s just not worth it – but do so knowing why and be able to live with the consequences.
Lastly chunk the work if possible – In rare cases and with smaller apps, you might be able to sit down and bang out a new app or fix an old one in a couple days or a week. For any real significant change though, you’ll want to have a game plan. If a refactor is in the cards, try to split the work into reasonably sized pieces that can be potentially released separately to the appropriate app store. If you’re scrapping the app and performing a rewrite, you may need a project plan and a more concrete idea of the steps you will take. Consider a minimum viable/lovable product (MVP/MLP) approach where you release just enough to satisfy your customers while still maintaining velocity.
Future-Proof Your New Code
Think about why your current app is in its current state. Did someone leave old code unattended for too long? Was everything so tightly coupled that pulling one thread of code out collapsed the whole app? Or maybe the app just grew organically into an unmanageable Swiss Army knife of features that no one could find or use?
It seems pretty obvious but try not to make the same mistakes again. Allow for periodic maintenance and schedule regular high-level app reviews to flush out any growing technical debt. Architect new features using patterns that allow for decoupling and replacement of app components without impacting the entire app. The period after WWDC and I/O always bring changes in the mobile app space so these can also be good times to start thinking about the future.
I’m not sure about you; but one of the reasons I absolutely love the mobile application space is the relentless, rapid pace of change and learning. Even if you think you’ve written the “perfect” app, it will need to updated. Apps are not “fire and forget” pieces of software despite being deployed out to the app stores all at once. Account for the related maintenance, updates, and even rewrites – and learn to enjoy it. It’s fun and challenging!
One thought on “Refactor or Rewrite? Tackling Legacy Apps”