Migrate from a Legacy System to Stigg
Learn how to migrate from a legacy entitlement or subscription management system to Stigg.
Why migrate?
Legacy entitlement and subscription management systems often create hidden friction: months-long engineering cycles for simple pricing changes, brittle integrations with billing providers, and poor support for usage-based models. A modern system can eliminate these bottlenecks and unlock faster product iteration, better developer experience, and enterprise readiness.
How to migrate?
Migrations from legacy entitlements and subscription management systems are often seen as long, risky, and complex - especially for large and enterprise customers. But with the right strategy and tools, it doesn’t have to be that way.
At Stigg, we’ve guided multiple successful migrations from legacy and in-house solutions, helping teams modernize their infrastructure while minimizing disruption. Based on these experiences, we’ve outlined a practical approach to help you plan and execute a smooth migration.
Our recommended strategy follows the Strangler Fig pattern, gradually replacing functionality from the legacy system rather than attempting a risky “big bang” cutover. The idea is to wrap the old system with new capabilities, then incrementally shift functionality until the legacy system is no longer needed.
This guide walks through the migration process in four stages:
- Implement dual-writes and backfill - Start by writing new data (e.g. customers, subscriptions, usage records) to both systems. Then, backfill historical data to populate the new system and keep both systems in sync.
- Rollout gradually - Use feature flags to route specific customers through Stigg, enabling a safe and incremental rollout.
- Monitor discrepancies with shadow-reads - Continuously verify that the new system behaves as expected before expanding rollout.
- Switch and sunset - Once confidence is high, fully switch over and decommission the legacy system.
We’ll use a real-world example of migrating from an in-house system connected to a billing provider (like Stripe or Zuora), or even a custom-built billing solution, and explain how to adopt Stigg as the new source of truth for subscription and entitlement management.
1. Implement dual-writes and backfill
The first step in the migration is to run both the legacy system and Stigg in parallel. This starts by setting up dual-write logic ensuring that any new data (such as customer, subscription, or usage records) is written to both systems simultaneously.
Here’s how to approach this phase.
1.1. Dual-writes
Update your application so that whenever a record is created or updated in the legacy system, a corresponding API call is also made to Stigg. This guarantees alignment between the two systems for all new data.
This means calling Stigg’s provisionCustomer, updateCustomer, provisionSubscription, updateSubscription, cancelSubscription, and reportUsage APIs every time a customer, subscription, or usage record is modified - whether by user action, automated triggers, or billing events (like payment success or failure).
For example:
When a new customer signs up or their subscription is created in the legacy system:
- Call
provisionCustomer
to create the customer in Stigg - Call
provisionSubscription
to create the corresponding subscription - If usage metering is relevant, report any initial usage with
reportUsage
1.2. Backfill background job
Once dual-writes are live, you’ll need a background job to backfill historical data and ensure records remain in sync between the legacy system and Stigg. Bulk import APIs can help speed up the process. It is highly recommended to test the import script in the development environment before executing any imports in production.
This job should perform the following tasks:
- Import customers into Stigg, including any available payment method identifiers from your billing provider if needed. Skip customers that were already backfilled (e.g.
customer.enrolled == true
). - Import active subscriptions so for each active subscription in the legacy system, create a corresponding subscription in Stigg. The recommended approach is to use newly created SKUs (defined in Stigg’s catalog) and backdate the subscription start date so no payment is collected during the import. Leave the legacy SKUs in the legacy system to avoid complex ID mappings and potential mismatches. Once the new subscriptions become active in Stigg, the system will automatically cancel the legacy subscriptions. For details, refer to Stigg’s import guide for paid plans.
- Import feature usage by reporting the most recent usage records for each customer. Stigg will only overwrite existing usage values if the imported timestamp is more recent, ensuring data accuracy without duplication.
Typically, there is a single active API key for each Stigg environment. To avoid hitting rate limits in production, please contact Stigg support to issue a dedicated key for the import process.
This setup creates a reliable foundation where both systems stay in sync, while allowing you to begin routing selected traffic to Stigg safely in the next step.
2. Rollout gradually
The next step is to start gradually shifting traffic to Stigg. This is done by implementing routing logic, typically using a feature flag or configuration toggle that determines which system, Stigg or the legacy one, should manage each customer’s subscriptions and entitlements.
Here’s how to do it:
- Control routing by introducing a customer-level feature flag (e.g.
use-stigg
) that determines which system should be used for subscription and entitlement operations. You can roll this out gradually by targeting specific segments, such as internal users, early adopters, or new signups before expanding to broader cohorts. - Implement a routing abstraction layer, so instead of scattering conditional logic throughout your codebase, introduce a centralized abstraction (e.g. a billing service interface). This wrapper can delegate to either the Stigg implementation or the legacy one based on the feature flag. Webflow used the Strategy Pattern here to great success, enabling zero-downtime switching between implementations.
- Ensure that the feature flag logic used for routing aligns with Stigg’s customer enrollment status. For example, when turning on the
use-stigg
flag, double-check that the corresponding customer and subscription already exist in Stigg (created either via dual-write or the backfill job). If not, trigger a backfill before proceeding to avoid inconsistencies.
With routing in place, you can start slowly moving customers to Stigg with confidence. If issues arise, simply toggle the feature flag off for a specific segment and fall back to the legacy system without user disruption.
3. Monitor discrepancies with shadow-reads
You’ll want to validate that Stigg behaves exactly like the legacy system before switching traffic over fully. This is where shadow reads and proactive monitoring come in.
3.1. Shadow-reads during rollout
For customers who are routed to Stigg, start silently calling the legacy system in the background for the same entitlements and usage data, without affecting user experience. Then compare the results between both systems, by introducing a comparison step in your abstraction layer to detect mismatches between the legacy system and Stigg.
When discrepancies are found, log them with enough detail to diagnose the root cause (e.g. customer and feature IDs, response deltas).
3.2. Trigger alerts for unexpected differences
If the comparison reveals differences that could affect the customer experience (e.g., missing features, misaligned limits), trigger an alert. Until these mismatches are resolved, fall back to legacy responses in production when discrepancies are detected. This ensures correctness and provides time to investigate edge cases.
It’s helpful to create custom metrics and monitor how many customers are running through Stigg, how many are still in shadow-read-only mode, and how often mismatches occur. Making this data available in a dashboard provides high value by offering a clear view of progress, surfacing issues, and helping prioritize fixes.
4. Switch and sunset
Once Stigg has been fully validated through shadow reads and the gradual rollout has proven stable, it’s time to complete the migration and retire the legacy system.
4.1. Switch, but verify
When you’re confident that Stigg has reached data parity and is functioning as expected, enable the use-stigg
flag globally. From this point forward, all subscription and entitlement flows will be powered by Stigg.
Proceed to gracefully cancel legacy subscriptions. For customers whose subscriptions are now managed by Stigg, make sure to schedule cancellations in the legacy system to avoid duplicate charges or conflicts.
If Stigg created a backdated subscription using its native billing connector, the legacy subscription can typically be cancelled once the corresponding Stigg subscription is confirmed to be active.
Keep monitoring the system for edge cases or inconsistencies. Continue logging metrics such as errors and mismatches to catch regressions early. Even after full cutover, it’s helpful to retain some validation logic temporarily.
4.1. Sunset
Decommission the legacy system in phases, break down the shutdown into smaller steps:
- Turn off writes to the legacy system, then eventually disable reads.
- Clean up infrastructure and code only after you’re confident nothing is calling it anymore.
- Let relevant teams know the migration is complete and share outcomes (e.g. performance wins, operational improvements, rollout stats). This helps build confidence in the new system and encourages adoption of the same approach in future migrations.
By taking the time to validate every step and gradually remove dependencies, you can safely deprecate the legacy system without downtime or customer impact.