Menu
Back to writing

Development

What happens to the users when you retire a product?

Publishing an announcement that your startup is joining another platform is easy. The real engineering work is ensuring legacy store links, user identities, and creator content don't break when the old servers wind down.

What happens to the users when you retire a product?

Writing an announcement that a product is being consolidated into a newer platform takes about thirty minutes.

Dealing with what happens to the existing users, their data, and their live links takes weeks of careful engineering.

When I announced that Shopmatey was joining Omni in April 2025, the business decision was clear. But from a technical perspective, Shopmatey was not just an abstract idea. It was a production system with real accounts, product databases, uploaded media, and public storefront URLs.

Creators had pasted links like app.shopmatey.com/kenneth/ into their Instagram bios, TikTok profiles, Linktree cards, and business pages.

If you simply shut off the servers or point the domain to a generic splash screen, you break every one of those links. You turn working creator businesses into 404 error pages.

I have seen plenty of startups pull the plug on a product with thirty days of notice, leaving users to scramble and dead links scattered across the internet.

I wanted to do the opposite.

Consolidating Shopmatey into Omni meant finding a way to retire the standalone infrastructure without abandoning the people who had trusted the platform.

The three things you cannot break

When you retire a user-facing tool, there are three primary assets that require preservation:

  1. User identity: The person should not lose their account history, preferences, or login credentials.
  2. Public URLs: Inbound links to profiles, storefronts, and products must continue resolving.
  3. User-generated content: Uploaded photos, product descriptions, catalog data, and media must remain accessible.

If you break any of those three, you are not absorbing a product. You are simply deleting it and forcing users to restart from scratch.

Legacy System (Shopmatey)              Target System (Omni)
┌─────────────────────────┐            ┌─────────────────────────┐
│ app.shopmatey.com/user  │──Redirect─►│ omni.channel/user       │
├─────────────────────────┤            ├─────────────────────────┤
│ Shopmatey Account ID    │──Mapped───►│ Canonical Omni User ID  │
├─────────────────────────┤            ├─────────────────────────┤
│ Product & Media Catalog │──Migrated─►│ Omni User Content Store │
└─────────────────────────┘            └─────────────────────────┘

Mapping identity without forcing re-registration

In a previous essay on unified authentication architecture, I described how I built an identity boundary that separated authentication providers from canonical internal user IDs.

That architecture proved essential during the Shopmatey consolidation.

Because user records in Appwrite were keyed to internal IDs rather than hardcoded vendor SDK types, migrating a user from Shopmatey to Omni did not require them to create a brand-new username and password.

The migration followed a straightforward resolution logic:

  1. Account lookup by verified credential: When an existing Shopmatey user logged into Omni using their verified email, Apple ID, or Google account, the system recognized the matching identity.
  2. Identity mapping: The backend linked the existing Shopmatey user record to their canonical Omni profile.
  3. Data association: The user's historical storefronts, bio configurations, and uploaded assets were automatically associated with their Omni identity.

To the user, the transition felt natural: they simply signed into Omni, and their existing data was waiting for them.

They did not have to contact support, export CSV files, or fill out migration forms.

Migrating relational data into unified content schemas

The database challenge was translating Shopmatey's e-commerce data structures into Omni's flexible content model.

In Shopmatey, product catalogs were organized around traditional e-commerce concepts:

  • merchant store profiles;
  • product variants and pricing tiers;
  • inventory quantities;
  • shoppable media posts.

In Omni, user-generated content was structured around rich cards, social feeds, and conversational widgets.

Rather than maintaining a legacy relational database running on a separate server, I wrote migration scripts to transform Shopmatey product documents into Omni's native card format:

Shopmatey Schema                      Omni Schema
┌────────────────────────┐            ┌────────────────────────┐
│ collection: products   │            │ collection: user_cards │
│ - store_id             │──Transform►│ - user_id              │
│ - title / price        │            │ - card_type: "product" │
│ - image_url            │            │ - payload: { ... }     │
└────────────────────────┘            └────────────────────────┘

By normalizing the legacy data into Omni's standard schema, the product listings became immediately compatible with Omni's search, rendering components, and feed algorithms.

Existing merchants did not have to re-upload their photos or retype their product descriptions. Their store items were already live inside the new ecosystem.

Preserving legacy URLs at the edge

The biggest operational challenge was handling legacy web traffic.

Shopmatey hosted storefronts on a dedicated subdomain:

https://app.shopmatey.com/[username]/

Creators had shared these URLs across social media for years. If someone clicked a link in an old YouTube description or an archived blog post, that request still hit app.shopmatey.com.

Keeping a full-blown application server running indefinitely just to handle legacy requests is expensive and unnecessary. You have to maintain server instances, keep databases online, and monitor background workers for a product that is no longer accepting new registrations.

The solution was to push the routing logic to the network edge using lightweight serverless functions and CDN rules:

Incoming Request: https://app.shopmatey.com/kenneth/


          Cloudflare Edge Worker

         ┌───────────┴───────────┐
         ▼                       ▼
  Recognized Path?          Static Asset?
         │                       │
         ▼                       ▼
  301 Permanent Redirect   Serve from Object
  to omni.channel/kenneth/   Storage / Cache

By handling redirects at the edge:

  • Inbound creator links were immediately forwarded with permanent HTTP 301 redirects to their new Omni profile.
  • Search engines transferred indexing authority to the new URLs.
  • The heavy Shopmatey backend servers could be safely spun down because the edge proxy handled all traffic routing in milliseconds.

The entire redirect infrastructure cost virtually nothing to operate, while ensuring that not a single external link was broken.

Media assets and object storage permanence

Another area where product shutdowns frequently fail is media asset hosting.

When users upload product photos, banners, and profile pictures, those files live in object storage buckets (such as Amazon S3, Cloudflare R2, or Appwrite Storage). If you shut down the application without planning for asset longevity, every image embedded across the web turns into a broken image placeholder.

To keep media persistent without paying for active app servers:

  1. Bucket migration: All legacy media assets were synced to a permanent, low-cost object storage bucket behind a global CDN.
  2. Path preservation: Media URL structures were kept predictable so that legacy image links continued to serve cached assets directly from the edge.
  3. Decoupling from the app server: The media endpoint had zero dependencies on the legacy application backend, allowing images to load in under fifty milliseconds worldwide.

This meant a creator's product photo embedded in a year-old blog post or pin continued loading flawlessly without requiring an active Shopmatey web server.

Active write versus read-only history

A critical distinction during any product sunset is separating active write operations from read-only historical access.

A live e-commerce platform requires complex write pipelines:

  • payment processing webhooks;
  • order status tracking;
  • inventory updates;
  • user registration forms;
  • real-time push notification dispatchers.

Once active development on Shopmatey ended, keeping those write pipelines open was a liability. It risked abandoned carts, stale payment integrations, and outdated terms of service.

The consolidation process moved the legacy system through two distinct phases:

Phase 1: Graceful write freeze

New store creations and payment processing on the standalone Shopmatey app were disabled with clear in-app messaging directing users to Omni. Existing sellers were given a transition window to complete active orders.

Phase 2: Read-only persistence

Product catalogs, images, and public profile data were snapshotted and integrated into Omni's content delivery infrastructure. The old write endpoints were decommissioned, while the public data remained accessible through the edge redirects.

This approach eliminated operational maintenance without erasing the historical record.

What I learned about sunsetting software

Retiring a product is often treated as an afterthought in software development. We spend months planning launches, but treat shutdowns like an embarrassing secret to be buried quickly.

Building this migration taught me three lasting principles:

1. URLs are a permanent promise

When you give users a public link, you are making a commitment that the link will work. Breaking URLs because you reorganized your internal company structure or changed your tech stack is disrespectful to the people who shared your work. Edge redirects make preserving links cheap and permanent.

2. Identity should outlive individual products

If your user model is tightly coupled to a single product's database table, migrating users to a new project is painful. When you build with a centralized identity layer, products become interfaces that connect to a user, rather than containers that trap them.

3. Decommission cleanly

Leaving half-abandoned servers running in the background because you are afraid to touch the database is not maintenance; it is neglect. Take the time to archive the data, redirect the routes, and turn off the unused machines.

Sunsetting a product properly takes deliberate engineering effort.

When you do it right, your users do not feel like they were abandoned.

They feel like they were brought along for what comes next.

Back to work.