SaaS Legacy Modernization Case Study: One Codebase, 200 Clients, Zero Downtime
A legacy modernization case study: how a SaaS platform serving 200 clients migrated to a plugin architecture with zero downtime and 70% faster deployment.
In this article:
- Context: a SaaS platform that had outgrown its architecture
- The core problem: client customisation without isolation
- The solution: migrating to a plugin architecture
- How we achieved zero downtime across 200 clients
- Results: 70% faster deployment and a scalable foundation
- Conclusion
This legacy modernization case study documents a migration that Eden Technologies completed for a SaaS platform serving 200 enterprise clients. The platform had been built as a standard multi-tenant monolith and had grown through years of client customisations that were progressively embedded directly into the core codebase. By the time the project began, the architecture made it nearly impossible to change anything without risk of breaking client-specific behaviour. Deployments required extensive manual testing and coordination, the pipeline was slow, and the team was reluctant to introduce any change that touched the most critical modules. The outcome was a migration to a plugin architecture, zero downtime for all 200 clients, and a 70 percent reduction in deployment time.
Context: a SaaS Platform That Had Outgrown Its Architecture
The platform had started as a clean multi-tenant application with a single codebase serving all clients. This model worked well initially. Client requirements were similar enough that most features could be shared, and deviations were handled through configuration.
Over several years, client requirements diverged. Some clients required behaviour that was specific to their industry. Others had data handling requirements that differed from the standard implementation. Each of these was solved at the time with the lowest-friction approach available: conditional logic in the core codebase, parameterised behaviour flags, and in some cases, per-client branches that were manually merged at deployment time.
By the time Eden Technologies was engaged, the codebase had accumulated hundreds of client-specific conditionals across dozens of modules. The test suite could not cover the combinatorial space of client configurations. A change intended for one client had a non-trivial probability of affecting another. Deployments were events that required a checklist, manual smoke tests across a sample of client environments, and a rollback procedure that had been used multiple times.
The engineering team knew the architecture needed to change. The question was how to change it without disrupting the 200 clients who were using the platform in production.
The Core Problem: Client Customisation Without Isolation
The root cause of the platform’s fragility was the lack of isolation between client customisations and the core product. Customisation had been implemented as modification rather than extension. Each client’s specific behaviour was woven into the core code rather than isolated behind a stable interface.
This created several compounding problems.
Cognitive load. Developers needed to understand the full set of client configurations when modifying any significant module. A change that appeared straightforward could have unexpected effects on clients with specific flag combinations.
Testing surface. The number of configuration combinations to test grew with each new client customisation. Manual testing at deployment time was the only practical verification mechanism, which made deployments slow and error-prone.
Onboarding new clients. Adding a new client with specific requirements meant modifying the core codebase, which touched the existing 200 clients. The cost of onboarding new clients was high, and the risk of breaking existing clients was real.
Dependency on senior knowledge. The complexity of the configuration system meant that only a few engineers fully understood how client behaviour was controlled. This created a single-point-of-knowledge risk that affected both delivery capacity and business continuity.
A software due diligence assessment confirmed these structural problems and provided the basis for scoping the migration work.
The Solution: Migrating to a Plugin Architecture
A plugin architecture addresses the isolation problem by defining a stable core that exposes extension points. Client-specific behaviour is implemented as plugins that interact with the core through defined interfaces rather than by modifying core code.
The migration required three components:
Defining the extension points. The first step was identifying every place in the core codebase where client-specific behaviour was implemented. This produced a map of over 60 extension points across the platform. These were then categorised by the type of customisation they enabled: data transformation, UI configuration, workflow modification, and integration with client-specific external systems.
Designing the plugin interface. Each extension point was converted to a stable interface that plugins would implement. The interface design required careful thought about what information plugins would need and what side effects they were allowed to have. The goal was to make the interfaces expressive enough to implement all existing client behaviour, while restrictive enough to prevent plugins from coupling to internal implementation details.
Migrating client behaviour. Each client’s customisations were extracted from the core codebase and reimplemented as plugins. This was done incrementally, one client at a time, with each migration validated in a staging environment that mirrored the client’s production configuration before deployment.
How We Achieved Zero Downtime Across 200 Clients
Zero downtime migration at this scale requires a strategy that keeps the system operational at every step. The approach used was a parallel execution model: during the migration period, each extension point ran both the old implementation and the new plugin, and the results were compared.
For each extension point, the migration proceeded as follows:
- Implement the plugin interface and create the plugin for each client.
- Deploy the change with both paths active. The old path continues to serve real traffic. The new path runs in shadow mode, logging any differences between the outputs.
- Monitor the shadow outputs for a defined period. Differences indicate bugs in the plugin implementation.
- Once the shadow outputs are consistent with the original for a validation period, switch the extension point to use the plugin exclusively.
- Remove the old code path.
This approach meant that at no point was any client served by unvalidated code. The old behaviour continued unchanged until the new behaviour had been validated against it. Rollback at any step required only a configuration change, not a deployment.
The migration took approximately four months from start to complete cutover. Each week, a batch of extension points was migrated. The team built confidence progressively, and the process became more efficient as they developed tooling to automate the shadow comparison and validation.
Results: 70% Faster Deployment and a Scalable Foundation
After the migration, deployment time dropped by 70 percent. The primary driver was the elimination of the manual smoke testing phase. With each client’s behaviour isolated in a plugin, changes to the core did not require validation across all client configurations. Changes to a specific client’s plugin required validation only for that client.
The CI pipeline benefited as well. The test suite could now test the core and each plugin independently. The combinatorial testing problem was replaced by compositional testing: each component was tested in isolation, and integration tests covered the interactions at the defined interfaces.
Onboarding a new client no longer required modifying core code. The plugin template gave new clients a starting point, and their specific requirements were implemented as extensions without touching the existing codebase. The onboarding process became faster and lower risk.
The platform’s legacy modernization had a secondary benefit that was not in the original scope: it made the codebase significantly more accessible to new engineers. With the complexity of client configuration isolated in plugins, the core codebase became readable and understandable. New developers could contribute to the platform without needing to understand the full matrix of client configurations.
Conclusion
This legacy modernization case study demonstrates that large-scale architectural change is achievable without service disruption. The keys were: a clear diagnosis of the root cause, an architecture that solved the structural problem rather than managing its symptoms, and a migration strategy that kept the system operational throughout.
The combination of zero downtime for 200 clients and 70 percent faster deployment shows that stability and speed are not competing goals. They are both outcomes of better architecture.
Does your codebase have these problems? Let’s talk about your system