CompertMe

Case study 08 · Consumer retail websites · Build, extend and maintain

Three consumer brands, one website engine, and a catalogue that keeps working when the phone has no signal

Because the moment that matters most is a customer standing in the shop with one bar.

Written by Hesham Ghoniem, lead engineer ·

Sector
Consumer retail websites · multi-brand
Engagement
Build, extend and maintain
Scope
Public storefront engine · three brands · installable app · search visibility
Stack
PHP
FastRoute
MySQL
Progressive Web App
Vanilla JavaScript
  • 8 — Files that diverge between two live brands, of 60
  • 3 — Brands running on one engine
  • 22 — Settings that define an entire brand
  • 671 — Files cached so the catalogue works offline
  • 379 — Pages published to search engines

Overview

Three brands needed a website each. We built one engine.

A retail group with several brands needed a proper website for each one. Each brand has its own name, city, price point, slogan and personality, but they all have the same job to do: show live stock, help someone choose, and get them through the door.

Building three websites would have solved the problem on the day it was asked and created a permanent one. Three codebases means every fix done three times, forever.

We built one engine and made a brand a settings file. Twenty-two values (name, address, phone, starting price, slogan, state, social accounts, financing partner, theme) and the same engine serves an entirely different brand. Across the two live brands, 52 of 60 program files are byte-for-byte identical, 87% of the engine. The differences amount to eight files, and each one is a genuine, deliberate brand difference rather than a copy that drifted.

The challenges

Five problems, starting with the cost of the third website.

Three websites means three times the maintenance, forever

A new feature, a legal disclaimer, a security fix: each would need doing three times, tested three times, with three chances to get it wrong. The cost of the third website is not building it. It is living with it for five years.

And the cost compounds invisibly. The three copies drift, and a year later nobody can say confidently what is different between them.

That is not a hypothetical. In this same client's back-office systems, two copies of the same software drifted 160 files apart, a story told in its own case study.

Related case study: Multi-Store Retail Operations Platform

The brands genuinely are different, and cannot be flattened

Different names, addresses, phone numbers, starting prices, slogans, social accounts, colour themes, finance providers, and different jurisdictions with different legal wording.

A shared engine that forces every brand to look and read the same is worthless, because the brands exist precisely because they are not interchangeable. The engine had to absorb real difference without forking.

The most valuable moment has the worst connection

The highest-intent moment in this business is a customer standing inside the shop, or in the car park outside it, looking at their phone while deciding. Shop interiors have poor signal. That is where the sale is won or lost.

A website that stalls at exactly that moment has failed at the only moment that really counted.

Stock changes hourly and the website must never lie

Live animals are single items. When one is sold it is gone, and a website still advertising it produces an angry phone call and a lost customer.

Conventional websites hold their own copy of the catalogue and refresh it on a schedule, which means they are wrong somewhere between every refresh.

For a business selling one-of-a-kind stock, "wrong for the next fifteen minutes" is not acceptable.

Customers do not shop by product name

Nobody searching for a family pet types a breed name. They arrive with constraints: someone in the house has allergies, they live in a flat, it is their first time, they travel a lot.

The catalogue had to be browsable by those constraints, in combination, and had to do something sensible when a combination matches nothing, because an empty page ends the visit.

Installable app · 671 files on the phone

The catalogue that works with no signal

Most websites need the internet. That is such a basic assumption that it is rarely stated, and it is exactly the assumption that fails at the moment this business most needs it to hold.

So each brand's storefront is built as an installable app. A customer can add it to their phone's home screen, where it sits with an icon and a name like any other app. There is nothing to download from an app store and nothing to approve.

And then it prepares for the signal to fail. In the background, before the customer ever needs it, the site quietly copies 671 files into the phone's own storage: every category illustration, every icon, every stylesheet, every script, every font, and the key pages of the catalogue.

By the second or third page, the phone is holding the shop. The customer walks inside, loses signal entirely, and keeps browsing.

Full signal, one bar, no signal

Three phone frames side by side, at full signal, one bar and no signal. The customer is browsing the same catalogue page in all three, each marked as working, and nothing about the third frame looks degraded. Under the third: 671 files already on the device.

The design decisions that made this possible are all refusals

  • No heavyweight framework. The entire front end is plain JavaScript. This is unfashionable and it is correct: a framework is hundreds of kilobytes that must arrive over the exact connection we have assumed is broken, on a phone that may be five years old.
  • Versioned caching. A new release replaces the stored copy cleanly instead of leaving customers on a stale version indefinitely, the failure mode that gives offline apps their bad reputation. In the reviewed code, the storefront runs version 2.7 of its cached bundle.
  • An explicit cache, not an automatic one. A named list of what must survive the loss of a connection, decided deliberately rather than whatever happened to be visited.

The website asks for the connection once, early, while the customer still has one, and then it stops needing it.

— Hesham Ghoniem, lead engineer

What we built

Six chapters, from a brand in one settings file to a site that is never behind the shop.

Chapter one · White-label configuration

A brand became a settings file

Everything brand-specific was pushed into one file of 22 values: name, address, phone number, email, starting price, slogan, state, social accounts, financing partner link, footer text, the staff-system address, and which visual theme to load.

Change those twenty-two values, plus the database settings that sit beneath them, and the same engine serves a different brand. Adding a brand does not involve a developer copying a folder.

One settings file, three storefronts

One settings file of 22 values with three arrows out to three different storefronts: brand one with its own name, city and colours; brand two with its own name, price and finance partner; and brand three, unbranded, with two colour themes side by side. The same engine serves all three.

Same engine.

Chapter two · Measured, not claimed

Proving it, by measuring the difference

A white-label claim is easy to make and easy to quietly break. So it is worth stating exactly what "shared" means here.

Across the two live brands, the engine holds 60 program files. Eight of them diverge. Every one of those eight is a real brand difference: a region-targeted landing page for one brand, a promotional page for the other, and small differences in contact handling and catalogue filtering.

52 of the 60 files are byte-for-byte identical: 87% across the full set, and 93% of the files that exist in both brands. A fix written once genuinely lands in both places.

Chapter three · The brand that belongs to nobody

A third brand that exists to keep us honest

The group also runs a neutral, unbranded installation of the same engine. It has no real content. It is a template, with two colour themes side by side.

It is there so that a new brand starts from a clean, brand-free base, and so that any assumption that leaks into the shared engine shows up immediately in the version that belongs to nobody. It is the difference between claiming the platform is white-label and continuously checking.

Chapter four · One source of truth

The website does not have its own copy of the stock

This is the simplest and most important architectural decision on the page, and it is the opposite of what most agencies build.

The public website and the shop's back-office system share one database. The website does not import the catalogue, synchronise it, or refresh it on a timer. It reads the same records the staff are editing, and serves photographs from the same storage the staff upload to.

When a member of staff marks an animal sold, the website is already correct. Not within fifteen minutes: already. There is no synchronisation to fall behind, no import to fail overnight, and no window during which the website is confidently advertising something that is gone.

Chapter five · No dead ends

Browsing by constraint, with no dead ends

The catalogue is browsable by the things customers actually care about, combined freely: allergy-friendly, suitable for flats, good for first-time owners, travels well, active households, size, and pedigree versus mixed.

Filters combine as "any of these" within a category and "all of these" across categories, the arrangement people intuitively expect without being taught. And when a combination matches nothing, the catalogue widens the search rather than showing an empty page. An empty result is a visit that ends.

Chapter six · Search visibility

The boring work that brings people in

Structured information for search engines describing each store as a physical business with a postal address, map coordinates and opening hours. A hand-maintained index of 379 pages. Breadcrumb data that gives each page a readable path. An explicit, named list of which search engines and social platforms are welcome to read the site, rather than a blanket rule. Region-targeted landing pages for local searches.

Technical deep-dive · the 60-file diff

Inside the eight files that differ

Most white-label claims collapse under a diff. This one was measured, and the result is unusually clean, so it is worth walking through what the eight differences actually are.

60 engine files, two live brands

A grid of 60 small squares. 52 are one flat colour, marked identical. Eight are highlighted, each with a one-word label: promotional page, data loader, category-name list, region landing page, catalogue filtering, listings page, pagination and contact.

  • 3 Files for one brand only — A promotional landing page, its data loader, and a supporting category-name list. The other brand has no equivalent.
  • 1 File for the other brand only — A region-targeted landing page, built to be found by people searching for stock in that particular area.
  • 4 Files that differ in content — Catalogue filtering, the all-listings page, pagination and the contact page. Each differs in how stock is presented or how enquiries are routed.

That is the complete list.

Everything else (the routing, the catalogue logic, the detail pages, the enquiry capture, the search suggestions, the offline caching, the templating, the security handling) is one shared implementation.

Why this matters commercially, in one sentence: when a change is needed, we can say with certainty whether it affects one brand or all three, because the answer is in the file list rather than in somebody's memory.

The honest limitation

It belongs here rather than in a footnote. The views, the visible page templates holding each brand's copy, imagery and layout, differ substantially between brands. That is intentional and correct: it is where a brand's personality lives, and it is exactly what should not be shared.

But it means the accurate claim is "one shared engine, brand-specific presentation", not "one identical website with a new logo".

The engine is shared. The face is not. That is the product, and it is the only version of white-label that survives contact with real brands.

— Hesham Ghoniem, lead engineer

How we work

Eight rules, and half of them are refusals.

Build the second one before finishing the first

The fastest way to discover what is genuinely brand-specific is to try it early, while it is still cheap to change. Waiting until the first brand is finished means discovering the wrong abstraction at the most expensive possible moment.

Every difference becomes a setting, never a copied file

The rule was absolute: when a brand needed something different, the answer was a new configuration value. The moment the answer becomes "copy the file and edit it", the platform has quietly become three websites again.

Keep an unbranded version permanently

A neutral installation that belongs to no brand is where leaked assumptions show up immediately. It costs very little to maintain and it is the only thing that keeps a white-label claim true over years.

Assume the connection is broken

Deciding early which pages must survive with no signal shaped the entire front end, including the decision to use no framework at all. Offline was a requirement, not a feature added later.

Refuse the fashionable choice when the user's phone disagrees

Plain JavaScript on a five-year-old handset in a building with one bar beats a modern framework that has to arrive first.

Do not copy data you can read directly

The website shares the shop's database rather than importing from it. One source of truth removes an entire category of bug, the stale copy, instead of managing it.

Treat search visibility as engineering

Structured data, sitemaps, crawler rules and landing pages were built in from the start. Bolted on afterwards, they are always a compromise with decisions already made.

Never let a customer hit a dead end

An empty results page is a lost visit. The catalogue widens the search instead.

Technologies used

Eight layers, and what each one is for.

Engine

One codebase behind all three brands. 44 active public routes each.

  • PHP
  • FastRoute

Front end

No framework. Fast on cheap phones and weak signal: a deliberate refusal, not a limitation.

  • Vanilla JavaScript
  • Bootstrap grid

Offline

Installs to the home screen, caches 671 named files, and new releases replace the cache cleanly.

  • Progressive Web App
  • Versioned service worker

Data and images

The website reads the same records staff edit, and serves the photographs staff upload directly, already optimised. No import, no sync, no stale copy.

  • MySQL, shared with the back office
  • Shared storage with the staff system
  • WebP conversion

Search

Describes each shop as a physical business with address, coordinates and opening hours.

  • schema.org structured data
  • 379-page index
  • Explicit crawler rules

Discovery

Browsing by constraint rather than by product name.

  • Typeahead suggestions
  • Multi-tag filtering

Enquiries

A web enquiry becomes a lead in the shop's own system immediately.

  • Direct capture into the shop's customer records

Finance

Applications are handled entirely by the provider. We never touch credit data.

  • A regulated third-party finance provider

Automated testing

Automated tests check each change before it reaches production.

This is the stack as it stood in the reviewed code, last modified in March 2023.

How the platform works

Seven steps, from a visitor arriving to a lead in the shop's own system.

Step 1

A visitor arrives

The engine reads that brand's settings file and serves the right name, theme, contact details, starting price and financing partner.

Step 2

The site starts caching itself

In the background, 671 files move into the phone's storage. The customer notices nothing.

Step 3

They browse by constraint

Allergy-friendly, flat-suitable, first-time owner, travels well, size, pedigree: filters combine freely. If a combination matches nothing, the catalogue widens rather than showing an empty page.

Step 4

They open a listing

The full record: identification number, date of birth, colour, every vaccination given, worming, vet checks, health certificate, guarantees and transport options. It is the same record the shop's own staff maintain, not a marketing summary of it.

Step 5

Signal drops

They keep browsing. The catalogue is already on the phone.

Step 6

They apply or enquire

A finance application is handed to the brand's regulated finance provider. An enquiry lands directly in the store's back-office system as a new lead.

Step 7

Staff add a new arrival

Meanwhile, staff add a new arrival in the back office. It is on the public site immediately, because there is no copy to update.

Results

What the business can do now that it couldn't before.

Run three brands from one codebase

A fix is written once. 52 of 60 engine files are byte-for-byte identical between live brands, and the eight exceptions are documented rather than discovered.

Launch a brand as a configuration exercise

Twenty-two settings define a storefront. No fork, no copied folder, no new codebase to maintain forever.

Keep customers browsing when the signal drops

The catalogue is installable and browses with no connection: in the shop, the car park, or a basement.

Never advertise something that has sold

The website and the back office share one database. There is no synchronisation to fall behind.

Be found locally

379 pages in the published search index, structured business information with address, coordinates and opening hours, and region-targeted landing pages.

Let customers browse the way they actually think

By constraint, in combination, with no dead ends.

Turn an enquiry into a lead with no re-typing

Web enquiries land in the shop's customer records directly.

Keep credit data out of the estate entirely

Financing is handed to a regulated partner.

The engineering, in numbers

  • 3 Brands running on the engine
  • 22 Brand-identity settings defining a storefront
  • 60 Program files in the shared engine
  • 8 Files that diverge between the two live brands
  • 52 of 60 Engine files byte-for-byte identical (87%)
  • 44 Active public routes per storefront
  • 671 Files cached for offline use
  • 379 Pages in the published search index
  • 2.7 Cached-bundle version in the reviewed snapshot
  • 0 Front-end frameworks used

Honest status

"One shared engine, brand-specific presentation" is the honest claim. The engine (routing, catalogue logic, filtering, caching, enquiry handling) is shared, and the 8-of-60 figure is exact. The visible page templates differ substantially between brands, deliberately, because that is where brand personality belongs.

The code is a 2023 snapshot. The storefronts were last touched in March 2023, and the offline bundle version quoted (2.7) is that snapshot's. The legacy back office these sites share a database with runs on PHP 7.4 and Bootstrap 4, both long past end-of-life. Nothing on this page presents that stack as current.

Real-time updating is architectural, not a live feed. The site is always current because it reads the shop's own database directly, not because it receives push notifications. A real-time messaging service is configured in the codebase, but we found no evidence it is wired into the storefront, so we do not claim it.

Related work

All work

Next step

About to build a website for your next brand?

Work with an engineering team that builds one engine instead of another codebase, measures what is really shared, and designs for the customer standing in the shop with one bar.

Book a discovery call

Client anonymised · every figure counted from a code snapshot last modified March 2023 · October 2026