Every dealership had its own copy of the site. Launching a new one took days.
- 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
Dealership sites on the platform1
Beforea separate copy of the code per site
Aftermore than 450 on one codebase
MeasuredLaunching a new dealership2
Beforedays, and error-prone
Afterunder 30 minutes, done by sales and support staff
MeasuredUptime3
Beforedowntime under traffic spikes
After99.9%, held over 12 months
MeasuredDealer management and inventory providers4
Beforea different feed format from each
Aftermore than 15 normalised on the way in
MeasuredReleases5
Beforefeatures reached sites at different times
Afterevery site at once, with no interruption to service
Described
| Measure | Before | After | Evidence |
|---|---|---|---|
| Dealership sites on the platform1 | a separate copy of the code per site | more than 450 on one codebase | Measured |
| Launching a new dealership2 | days, and error-prone | under 30 minutes, done by sales and support staff | Measured |
| Uptime3 | downtime under traffic spikes | 99.9%, held over 12 months | Measured |
| Dealer management and inventory providers4 | a different feed format from each | more than 15 normalised on the way in | Measured |
| Releases5 | features reached sites at different times | every site at once, with no interruption to service | Described |
Four lines are measured. One describes how the system works now and carries no number. The small number beside each line says how it was measured.
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.
- What hurt in this state
- Built in this engagement
Three decisions, and what we turned down
The stack is the easy part to copy. The choices behind it are what made it work, so each one names what it replaced and what it cost.
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.
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.
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
Tenancy first, then the tools that put dealers on it, then what keeps it running.
Tenancy core
One codebase, the plugin override mechanism, a PostgreSQL schema per dealership and JWT authentication scoped per dealer.
Storefront and search
React with Next.js server-side rendering, Redis caching and Elasticsearch for vehicle search.
Inventory ingestion
Scheduled ingestion over APIs and flat files, with a custom transformer for each provider's format.
Self-service tools
The admin panel with guided wizards for launching a dealer, and the no-code visual builder for promotions.
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.
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
Every dealer management system sends its vehicle feed in a different shape, so each needed its own transformer. More than 15 providers means more than 15 ways for a field to be missing, renamed or formatted differently, and each surfaced when a dealer on that provider went live. Next time, each provider gets captured sample files and a test the transformer must pass before the first dealer on it launches, and a vehicle 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.
- Write down the hours the last launch took, and who had to do it.
- Count the copies of your code running in production.
- Pick your last fix and count how many sites have it today.
How it is built, 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
How each figure was measured
- Dealership sites live on the one codebase.
- Time to launch a new dealership: days before, under 30 minutes after, done by sales and support staff with the admin wizard.
- Uptime against the 99.9% service level, held over 12 months.
- Dealer management and inventory providers whose feeds are ingested and normalised.
- Blue and green releases with versioned database migrations, so a release does not interrupt the sites.
- A schema per dealership in PostgreSQL, with Redis, Elasticsearch and a no-code builder for promotions.
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.