Every dealership had its own copy of the site. Launching a new one took days.

An automotive retail marketing and technology provider runs a storefront for each of its dealership customers. We replaced a system that could not share code between them with one multi-tenant platform. More than 450 dealership sites now run on it, and sales staff launch a new one in under 30 minutes.
Client
An automotive retail marketing and technology provider
Scale
More than 450 dealership sites on one platform
Engagement
A replatform onto one multi-tenant codebase

The outcome in five lines

The outcome in five lines
MeasureBeforeAfterEvidence
Dealership sites on the platform1a separate copy of the code per sitemore than 450 on one codebaseMeasured
Launching a new dealership2days, and error-proneunder 30 minutes, done by sales and support staffMeasured
Uptime3downtime under traffic spikes99.9%, held over 12 monthsMeasured
Dealer management and inventory providers4a different feed format from eachmore than 15 normalised on the way inMeasured
Releases5features reached sites at different timesevery site at once, with no interruption to serviceDescribed

Four lines are measured figures from the engagement record. One describes a change in how the system works that carries no number, and is marked so. Each links to its source.

The problem, in the client's terms

The previous system had no multi-tenancy. Every dealership site meant another copy of the code, so a feature reached some sites and not others, at different times. Onboarding a new dealership was slow, and somebody usually made a mistake doing it. Updates and content went out unevenly. When traffic spiked, sites went down or slowed, and the client's own customers noticed.

For the people running it, every dealership was a small project. A launch meant a developer setting up a site by hand. A fix meant deciding which copies needed it and applying it to each. A question from a dealer about why a feature was missing had no simple answer, because the answer was different for every site.

Adding dealerships made all of this worse at the same rate. The work grew with the number of sites, not with the number of features, so growth itself was the cost.

  • ConstraintEvery dealership keeps its own brand and customisations.
  • ConstraintReleases may not interrupt service. The client committed to a high uptime level.
  • ConstraintLaunching a dealership has to be work that sales and support staff can do, not a software engineer.
  • ConstraintNo dealership may see another's data, stock or customers.

The architecture, before and after

Switch between the two. Separate copies of the code became one codebase with overrides, hand-built launches became a wizard, and every inventory feed now passes through one ingestion path.

Before. One copy of the code per dealership, launched by hand and updated site by site.
The architecture, before and after: BeforeBefore: One copy of the code per dealership, launched by hand and updated site by site. Components: A new feature, Dealer site A, Dealer site B, Dealer site N, New dealership, Traffic spike. A new feature connects to Dealer site A. A new feature connects to Dealer site B, dashed. A new feature connects to Dealer site N, dashed. New dealership connects to Dealer site N. Traffic spike connects to Dealer site A.A new featurecopied site by siteDealer site Aits own code copyDealer site Bits own code copyDealer site Nits own code copyNew dealershipdays, often with errorsTraffic spikedowntime, slow pagesEach site gets a changewhen someone copies itthere.The client's owncustomers noticed theoutages.
After. One codebase serves every dealership, isolated by schema and launched from a wizard.
The architecture, before and after: AfterAfter: One codebase serves every dealership, isolated by schema and launched from a wizard. Components: Cloudflare, NGINX cache, Next.js storefront, Redis, Admin wizards, Platform services, Plugin overrides, GKE and ArgoCD, Visual builder, PostgreSQL, Elasticsearch, Feed transformers. Cloudflare connects to NGINX cache. NGINX cache connects to Next.js storefront. Next.js storefront connects to Platform services. Admin wizards connects to Platform services. Visual builder connects to Platform services. Platform services connects to Plugin overrides. Platform services connects to Redis. Platform services connects to PostgreSQL. Platform services connects to Elasticsearch. Feed transformers connects to Elasticsearch. Feed transformers connects to PostgreSQL.Cloudflaremanaged certificatesNGINX cachereverse proxyNext.js storefrontReact, server renderedRedisin-memory cacheAdmin wizardslaunch in under 30 minPlatform servicesLaravel and Node.jsPlugin overridesper dealer, no forkGKE and ArgoCDblue and green releasesVisual builderpromotions without codePostgreSQLa schema per dealerElasticsearchvehicle searchFeed transformers15+ inventory providersA new dealer is a newschema and a wizard run.
  • What hurt in this state
  • Built in this engagement

Components are from the engagement record. The shape of the old system and the direction of each link are drawn from how the record describes them.

Three decisions, and what we turned down

What was built is in the record. Why it was built that way is the part a technical reader can judge us on, so each decision names what it replaced and what it cost.

  1. One codebase with plugin overrides, not a fork per dealer

    • Turned down

      A fork of the code for each dealership

      This was the old system's problem. Every fix is applied once per fork, and forks drift until no two sites behave the same.

    • Chosen

      One codebase, with a plugin-style override for what a dealer needs to differ

      A fix or a feature ships once and reaches every dealership. What a dealer changes is kept in one known place, separate from the core.

    What it cost: The override points had to be designed up front and kept narrow. A dealer who wants something no override point allows waits for a core change, and every core change has to be tested against the overrides in use.

  2. A database schema per dealership

    • Turned down

      Shared tables with a dealer column

      One query that forgets the dealer filter shows one dealership another's data, and one large dealer's load and indexes are everyone's.

    • Turned down

      A separate database per dealership

      Every dealer adds a database to provision, connect to, back up and migrate, and connection counts grow with the number of tenants.

    • Chosen

      One PostgreSQL server, one schema per dealership

      Each dealer's data sits apart by default, the platform still runs one database, and a dealership can be exported or removed as a unit.

    What it cost: Every migration runs once per schema, more than 450 times per release, and has to cope with a run that stops partway. Reporting across dealers means querying across schemas.

  3. Blue and green releases with versioned migrations, not maintenance windows

    • Turned down

      Scheduled maintenance windows

      A high uptime commitment leaves little room for planned downtime, and a storefront has visitors at every hour a window could use.

    • Chosen

      Run the new version beside the old, switch traffic once healthy

      A release does not take sites offline, and switching back to the old version is quick if the new one misbehaves.

    What it cost: Old and new code run against the same database during a switch, so every schema change must work for both. A breaking change takes two releases, and the platform needs room for two versions at once.

How it was delivered

The order of the work follows what depends on what. Tenancy first, then the tools that put dealers on it, then what keeps it running. The record holds no week windows, so none are shown.

  1. Tenancy core

    One codebase, the plugin override mechanism, a PostgreSQL schema per dealership and JWT authentication scoped per dealer.

  2. Storefront and search

    React with Next.js server-side rendering, Redis caching and Elasticsearch for vehicle search.

  3. Inventory ingestion

    Scheduled ingestion over APIs and flat files, with a custom transformer for each provider's format.

  4. Self-service tools

    The admin panel with guided wizards for launching a dealer, and the no-code visual builder for promotions.

  5. Platform and releases

    Google Kubernetes Engine with autoscaling, GitHub Actions and ArgoCD, blue and green deployments, and Prometheus with Grafana.

Who owns what now

The client's own sales and support teams launch new dealerships through the admin panel, and dealerships run their own promotions and landing pages.

Results

What changed for the client's staff, for the dealerships and for the people shopping on their sites.

  • Sales and support staff launch a new dealership through a guided wizard in under 30 minutes, without a software engineer.
  • Dealerships run their own promotions, banners and landing pages through a no-code visual builder.
  • The client's own team ship a fix or a feature once and see it on every dealership site at the same time.
  • People shopping for a vehicle meet a site that stays up when traffic spikes, held at 99.9% uptime over 12 months.

The old system's uptime and launch time were not recorded as figures beyond "days", so no before number is shown for either.

What it would be worth to you

We do not publish what an engagement costs. So this works from your side: what your client launches cost today, what they would cost at under 30 minutes, and, if you enter them, what your downtime costs.

Your launches today

from the engagement recordan assumption, change ityours to enter

Your uptime, optional

Under 30 minutes is counted as half an hour, the upper end of what the record states. The record says a launch took days, so three working days of eight hours and the hourly cost are our assumptions. Change them to yours. Downtime uses 8,760 hours in a year and the 99.9% this platform held over 12 months.

Launch labour saved per year

Enter your launches

Launch hours per year, today
Enter your launches
Launch hours per year, after
Enter your launches
Downtime hours avoided per year at 99.9%
Enter your uptime
Value of that downtime
Enter both uptime fields

A launch becomes a task for the sales team rather than a project for a software engineer.

What went wrong, and what we would change

Every inventory provider sent a different shape

The record says standardising vehicle feeds from diverse dealer management systems with inconsistent formats required custom data transformers. More than 15 providers means more than 15 ways for a field to be missing, renamed or formatted differently, and each one surfaced when a dealer on that provider went live. Next time, each provider gets a set of captured sample files and a test the transformer must pass before the first dealer on it launches, and a record that will not map is set aside for review instead of failing the whole feed.

An override can turn into a fork by another name

A plugin override is the right tool for a dealer's real differences. Left unwatched, overrides grow until one dealer's site behaves like a separate product again, which is the problem the platform replaced. What we would change: count overrides per dealer and review any that reach past presentation into business logic.

A migration has to succeed on every schema, not most

With a schema per dealer, one release migrates every schema. A migration that works on a test schema can fail on one dealer whose data is unusual, and the release is then half done. What we would change: rehearse each migration against a copy of every schema before the release, and make every migration safe to run twice.

Will this work for your platform?

It fits when

  • You run many sites for many clients, and most of what each client needs is the same.
  • You launch new clients often enough that the launch itself is a cost you notice.
  • Clients should not see each other's data, and you need to prove it.

Look elsewhere when

  • You have a handful of clients who each want something very different. Separate builds may cost less.
  • One client needs its own infrastructure by contract. A schema on a shared server will not satisfy that.
  • Your sites differ mostly in content, not in behaviour. A content system with themes may be enough.

Check it yourself after your next client launch

If a launch takes days and a fix reaches your sites at different times, this case study is about you. Put your launch count into the calculator above.

  1. Write down the hours the last launch took, and who had to do it.
  2. Count the copies of your code running in production.
  3. Pick your last fix and count how many sites have it today.

The stack, by layer

  • StorefrontReact with Next.js server-side rendering, a no-code visual builder for promotions
  • AdminA React admin panel with guided wizards for launching a dealership
  • PlatformLaravel and Node.js, one codebase with plugin-style overrides, JWT authentication with per-dealer scope
  • DataPostgreSQL with a schema per dealership, Redis for in-memory cache, Elasticsearch for vehicle search
  • IntegrationScheduled inventory ingestion over APIs and flat files, custom transformers for more than 15 providers
  • InfrastructureGoogle Kubernetes Engine with autoscaling, Cloudflare managed certificates, an NGINX reverse proxy cache
  • DeliveryGitHub Actions and ArgoCD, blue and green deployments, versioned database migrations, Prometheus and Grafana

Where each figure comes from

  1. Engagement write-up. "450+ active dealerships supported with customizable retail experiences" and "Multi-Tenant SaaS Engine: Built with Laravel and Node.js, enabling independent dealership sites to share a single codebase while supporting brand-specific customizations." The problem statement records that the previous system had no multi-tenancy, so each site meant duplicated code.
  2. Same write-up. "30-minute dealer onboarding reduced setup time from days to minutes" and "A React-based admin panel with intelligent wizards allowed sales and support teams to launch new dealers in under 30 minutes." The problem statement records onboarding as slow and error-prone.
  3. Same write-up. "99.9% SLA met consistently over 12 months". The problem statement records downtime and inconsistent performance under traffic spikes, which the record does not quantify.
  4. Same write-up. "Scheduled ingestion and syncing of vehicle inventory via APIs and flat file parsers, compatible with over 15 DMS and inventory providers." and "Standardizing vehicle feeds from diverse DMS systems with inconsistent formats required custom data transformers."
  5. Same write-up, described without figures. "With high SLA requirements, we implemented blue/green deployments and database migration versioning to avoid interruptions." The problem statement records features and updates reaching sites unevenly.
  6. Same write-up, described without figures. "PostgreSQL with multi-schema support per dealership, Redis for in-memory cache, and Elasticsearch for lightning-fast vehicle search." and "Dealerships could launch promotions, update banners, and manage landing pages through a no-code visual builder."

Figures we hold and do not show yet

  • Performance scores improved 60%. The record does not name the instrument or which scores. It publishes once the instrument is known.

Tell us how long your last client launch took, and we will tell you if one codebase pays.

Send the launch hours and the number of client sites you run. A software engineer reads it and replies with an honest answer, including when separate builds are the better choice.