Three consumer platforms for one operator
For one operator I became the sole full-stack engineer on three concurrent consumer directories. I owned the schema, back end and admin, and kept the sites in step with a shared toolkit instead of three copies.
A case study by Arif Hossen
Source: https://arifhossen.dev/work/three-consumer-platforms-for-one-operator
- Role: Sole full-stack engineer
- Team: The operator and me
- Stack: Laravel 8 to 10, MySQL, Tailwind, Alpine.js
- Period: Sep 2023 to Jun 2025
- Engagement: Independent
- 3 Platforms run concurrently for one operator
- 89% Of commits across the three repositories, 2,509 of 2,820
- 8 to 10 Laravel generations crossed, one version per step
- 13 Templated search page types on the largest platform
Three sites at once
Context
One operator ran three consumer directories that earned from search traffic and advertising. I started on front-end fixes and led the move from Bootstrap to Tailwind and Alpine.js on a live site. Then I was given full ownership: the schema, the back end, the admin tooling, the pages visitors see, media handling and the integrations behind them.
The operator designed in HTML and CSS and committed to the same repositories, so the history shows both of us. I wrote 2,509 of the 2,820 commits across the three. I do not name the platforms or the operator on this site, so I describe the work by what it was.
One toolkit, not three copies
Reuse
The three products needed the same things: search landing pages with editable metadata, owner-run advertising, image handling, member reactions, legal pages, site settings and a back office. I treated those as a toolkit. The page-metadata component, the image compression service, the reaction logic, the legal-document pages, the cached settings helper and a cache-purge helper exist in all three repositories.
Reuse ran in both directions. A search feature built for one site was ported to another, and ad units built for the siblings were later ported back to the first. A new product started from proven modules instead of a blank page, and the operator's team learned one back-office pattern for three sites.
The address structure is the product
Search
For a business funded by search traffic, the address of a page matters more than its design. A filter-driven directory can reach the same page under many parameter orders, which splits ranking across duplicates. I built a canonicaliser. Parameters in a non-standard order are permanently redirected to the single canonical form, and an unknown parameter returns not found instead of a duplicate. Every page carries a canonical tag, and a generated page map lists the real combinations.
Titles and descriptions come from templates with placeholders, one template per page type, with the available variables shown as hints on the admin form. The operator's team edits search copy for whole families of pages without me. Personal profile pages are marked no-index so they never compete with landing pages or expose members in search.
When one platform widened from a single source to many, the address pattern had to change on a site search engines already knew. I permanently redirected every old address to its new one, added a catch-all for legacy links, and fixed a redirect loop the day it appeared.
Reviews that exist before a page is claimed
Data model
On the review site, a page can exist before the person it describes has registered. Each page is pending, claimed or unclaimed. Visitors can read, rate and post a review without an account. A guest review waits as pending and publishes automatically when the reviewer signs up, so every published review belongs to a real account. The subject of the page can claim it later.
The point of the model is the funnel. A visitor is never blocked from contributing, and the site still ends up with an owner for every review.
Relaunching over a live database
Live data
One of the three already existed as a live site, with members, posts and search rankings, when I took it on. The operator wanted it rebuilt with a proper back office. Copying the data across at the end would have been a risky switch, so I took a snapshot of the live data and rewrote the whole set of structural migrations to start from it.
Legacy tables were renamed to the new vocabulary, dead columns dropped and integrity constraints added step by step. Two commits record the exact sequence as a runbook. The site came back on the same domain, on the new platform, with its existing members and addresses intact.
Video without blocking the site
Media
Members could post short videos. Video is heavy, so I kept it off the web server. A validation rule reads the file and rejects anything over 60 seconds or in an unsupported format. The accepted file is staged to cloud storage and handed to an external conversion service that returns two formats and a thumbnail.
<!-- diagram:video_pipeline -->
Diagram: The video pipeline
The web server validates and stages the file, then hands off. The post exists from the first step but only goes live when the conversion service calls back.
The post is created straight away but stays unpublished. When the service calls back on a dedicated endpoint, the post goes live and the temporary file is deleted. While it processes, only its owner can see it, with a message that says so. On the feed, videos play and pause by visibility so many on one page do not compete.
Staying current without a rewrite
Upkeep
The sites ran on a framework generation heading out of support, and the operator could afford neither a rewrite nor a feature freeze. I upgraded one major version a day, in isolated steps. A sibling site took the same two versions in a single day, a few weeks later. The styling toolkit had been moved with the same discipline. Two sites went from Laravel 8 to 10 without a freeze, and the commit history records no incident from either upgrade.
What the record does not show
What I would do differently
These repositories have no automated tests and I would not call them well covered. Quality came from small commits, a staging step and the operator looking at the pages. Writing tests around the address canonicaliser and the guest-review publishing would be the first thing I added today.
I also have no traffic, registration, advertising or revenue figures for any of the three, so I make no claim about outcomes. What this page can show is how the work was built, not what it earned.