← All projects Case study · Internal tooling

Search rollout

Search V2 existed. Getting it onto every eligible storefront was its own project. I wrote a TypeScript CLI that installs it through the loader each site already runs, without touching a single brand site, and dry-runs by default.

  • 271of 272 brands live
  • 7commands, dry-run default
  • 8status classes per brand
  • ~1.5klines TypeScript, no build step
  • Aug2026
Try it

Roll search out to an invented fleet

Run Discover, Plan, then Apply --apply and Verify. Click a brand to see its config diff and backup, or roll one back.

search-rollout · synthetic fleetInteractive demo
The interactive CLI needs JavaScript. It shows a terminal running discover, plan, apply, verify, deconflict, report and rollback against a fleet of invented brands, next to a live status board.
Brand names and ids are invented. The command set, status classes and safety rules match the real tool.
Problem

Installing a storefront script by hand on hundreds of brands means hundreds of chances to break someone's live site.

Each brand had its own search config, its own set of on-site widgets and sometimes the old search running unconditionally. Some configs were disabled, some search indexes were empty, some input selectors no longer matched the live site. Turning a shared status flag on would also switch the old search back on for every visitor.

My role

I designed and wrote the tool, ran the pilot, then the full rollout, and documented the traps it found. It talks to existing panel endpoints; nothing on the storefronts changed.

  • discover scans every customer id, checks search health and inventories existing widgets
  • plan writes a diff and sends nothing to production
  • apply stays a dry run until --apply, backs up each brand, writes, then reads back
  • verify checks config, widget, script host and search results independently
  • deconflict, report and rollback --confirm for the edge cases
Architecture

Brand by brand, reversible at every step

  1. DiscoverClassify every brand: ready, stale-selector, loader-unconfirmed, search-empty, disabled and more.
  2. PlanDiff for ready brands only. No requests to production.
  3. ApplyBackup, merge into the full config object, write, read back.
  4. Existing loaderThe widget loader each site already runs picks up the new search widget.
  5. Verify + reportIndependent checks and a one-file HTML report with test links.

Violet tint: parts I wrote.

Engineering decisions

What was hard and what I did

Safe by default

apply without --apply is still a dry run. Real writes take a flag and can be limited to a few brands with --limit or --ids, so a pilot runs on a handful of brands before the rest.

Never overwrite a brand's own work

The save endpoint replaces the whole config object, so the tool always reads, merges and posts the full object. Existing brand CSS is never overwritten, the old search CSS is never touched, and a disabled brand is never switched on silently.

One flag, two products

The old and new search read the same status flag. Turning it on for V2 would expose V1 to every visitor on some brands, so that path is off unless you pass --allow-v1-exposure, and those brands are listed instead.

Verify separately from apply

A read-back proves the write landed. verify proves the site works: the script host actually serves the file, and the search endpoint still returns products. A subdomain copied from an old widget once turned out not to exist in DNS.

Old widgets that fight the new one

Some brands ran their own old search widget on every page, so on the demo link both bound to the same input. deconflict adds a URL rule that disables the old one only on the demo link; normal visitors see no change.

Write down the traps

The loader caches the widget list for the whole session, so a test link opened in an old tab shows nothing. The pilot hit exactly that. The README now says to test in a new tab, with the reason.

Have a problem worth solving?Let's make it reliable

Start a conversation

Open to full-stack and applied AI roles, remote or hybrid.

Tell me about the role, the team and the problem.