Context
A managed home for other people’s AI agents.
Bring your own model provider, connect a messaging channel, and CrewM8 provisions an isolated container that runs your agent continuously.
- The team was three people. There is nowhere to hide on a team that size, and no one to hand things to.
- I joined as a generalist and ended up owning the product’s entire presentation layer and its billing surface.
- The tenure in numbers: 101 commits and 10 merged pull requests across four months.

What I owned · and what I did not
The front end, end to end. And a boundary I can defend.
- The marketing site and the onboarding flow behind it.
- The authenticated dashboard, rebuilt and shipped as one 22-commit pull request.
- The design system it was all built on, through the migration that retired shadcn and Radix.
- The Stripe integration: the billing surface, plus raw SQL and Drizzle queries against Postgres for account and credit resolution.
the boundaryBeing specific about it matters.
The infrastructure layer, the agent runtime, and most of the API belonged to another engineer. The CI/CD pipelines were written before my first commit.
My full claim there: I repaired the pipeline once, to unblock a merge. That is the extent of it.
Stack
Chosen to be reasoned about, not admired.
Flagship one · the dashboard rebuild · one PR, 22 commits
Every screen used to refetch the world.
The authenticated dashboard fetched per screen. Each navigation requested the same data again and blocked the UI on it. The rebuild replaced the data layer underneath every screen.
01Mount-once context providers.
Data loads once when the dashboard mounts. Every screen reads from context instead of fetching for itself.
Replaced: a fresh fetch per screen, per visit.
02Stale-while-revalidate caching.
Screens render the cached copy immediately and revalidate in the background. The user never waits on data that has not changed.
Replaced: blocking spinners in front of stale-safe data.
03Progressive load with cleanup.
A blocking load became a progressive one: skeletons while data settles, and in-flight requests properly cancelled on navigation.
Replaced: one all-or-nothing load, with abandoned requests left running.
the performance claim here is deliberately withheld · see the honest caveat at the end
Flagship two · the design system migration
Minus 3,054 lines. Nothing the user saw changed.
I migrated the product onto the in-house design system and retired the shadcn and Radix primitives, rebuilding the dashboard primitives on our own tokens with no change to the shipped surface.
Deleting code without changing behavior is the strictest test of whether you understand it. The diff closed at 3,054 net lines removed.
How I work · the same gates, every merge
Three gates. No exceptions for small changes.
Small changes were where the regressions came from, so nothing merged without passing all three:
- 1
tsc --noEmitat zero errors.Types are the first reviewer. - 2A passing production build.Not the dev server. The thing that ships.
- 3Lint warnings held at the agreed baseline.Held across all 10 merged PRs. Never allowed to creep.
- Every defect got a written entry: the root cause, two fix options with their costs, the gate that would verify the fix, and a logged RCA result.
- That log compounds. It became the reference the next time something similar broke.
- Before the largest release I read my own branch diff line by line and wrote up the findings. Items I chose not to fix were logged as tracked bugs, not quietly dropped.
One bug, close up · what the protocol catches
It looked like a server problem. It was a click.
draft deletion in the dashboard, root-caused and written up
Deleting a draft navigated away mid-request, which read like a backend failure. It was not. The click event was propagating from the delete control up to the enclosing card, which triggered a route change while the DELETE was still in flight. A React synthetic event bug, isolated on the client.
The write-up is the reason the same class of bug was easy to spot the next time it appeared.
One problem worth showing · sizing machines for headless Chrome
We assumed memory would be the ceiling. It was CPU.
We had to size the machines that would run headless browser workloads, and the working assumption was that memory would run out first. Instead of guessing, I stress-tested a single 1 vCPU instance to the point of failure.
- Load average hit 51 on one vCPU while memory never became the constraint.
- Per-tab memory fell from roughly 80MB to 30MB as the browser compacted itself under pressure.
- Chrome raises its own renderers’
oom_score_adj, so the kernel kills renderers first and the process looks healthy while quietly losing its work.
the output was a production sizing table the machine purchase was made against
Impact
What four months left behind.
The billing surface shipped on Stripe. P0 to P2 defects closed with a logged RCA each. Lint stayed at baseline across every merged PR. And the sizing recommendation inverted what the company was about to buy.
The honest caveat
One claim I will not dress up.
The dashboard performance work is a structural argument. Mount-once providers, stale-while-revalidate, progressive loading: I believe in the shape of it. But I did not instrument it, so I have no before-and-after timing to show you.
I would measure it first if I did it again.