This post originally appeared on a previous employers’ blog.
Legacy systems draw the ire of both developers and managers alike. For developers, these systems are demoralizing and can feel insurmountable. To managers, they pose substantial risks to reliability and velocity. For many companies, Remotion included, a complete re-write is costly and carries its own risks; The new system could take too long to build, discard important learnings, or end up just as complex as the original. A much better option to replacement is a retrofit.
Making judicious use of software engineering principles–e.g. abstraction, encapsulation and modularization–these retrofits can not only de-risk large changes, but also improve adjacent code quality.
Take for example, Remotion’s recent switch to Zoom. All A/V providers are not created equal–each with its own APIs, workflows and nuanced behavior. It was important for us to evaluate those differences without sacrificing our ability to alter course. Further, as a desktop app we couldn’t rely on a simple “deploy & forced refresh” scheme like a purely web-based app can. Any changes to our A/V stack would need to be gradually rolled out and negotiated between clients as they ‘caught up’ with newer versions.
Fundamental to this process is the notion of abstraction. In our case, comparing providers to highlight a common essence, while obscuring specific implementation details. (Arguably, this kind of work is really only possible with the benefit of hindsight. Abstracting without alternatives can lead to “leaky” or inflexible abstractions). Abstraction in hand, the first step yields benefits immediately, regardless of the outcome: encapsulating the existing provider creates a cohesive module decoupled from other parts of the system, hopefully creating a more singular-level of abstraction.
After encapsulation, or even concurrently, the next, most crucial step is adding a feature flag. Perhaps underutilized, feature toggling opens a plethora of possibilities; developers are free to experiment & evaluate liberally; new code is safe to merge without conflict; and, when it’s ready, internal teams or early adopters can opt-in during a gradual roll-out. Along with frequent releases and version metrics, it is even possible to negotiate providers to support older clients, automatically upgrade eligible teams/users, or in the worst case, back-out the change completely with the flip of a switch.
In summary, retrofitting legacy systems can be a more effective and cost-efficient option than completely re-writing them. Using software engineering principles like modularization and abstraction not only de-risks large changes, but also reduces painful merge-conflicts and deployments while improving existing code. Our recent switch to Zoom serves as a great example. Once we had validated our decision to switch, early adopters gave us valuable metrics & feedback we used to further improve the experience before a smooth broad release.