I write roughly zero percent of the code in my products now. Welcome to the future.

That is not a fun way of saying I use Copilot. I mean I do not write the implementation at all. I still read through a section when something feels wrong, inspect an architectural decision, or debug something weird data-side. But outside of API keys and environment configuration, I am not sitting there typing production code anymore.

My agent fleet does that for me. One of those products where they fleet out is DiveJourney.

DiveJourney is the global platform for dive travel: more than 12,000 dive spots and 234+ destination guides, trip planning, dive logs, and a network of divers and dive shops wherever you go. It is also the largest proving ground I have for what it looks like shipping a production-grade product solo that has become almost entirely agentically engineered.

By July 2026, saying that agents write the code is not as revolutionary as it would have sounded 8 months ago. But I have been moving toward this since December. It's only become easier as the models kept improving and the harnesses kept getting tighter. And so my job shifted beneath my feet, in the ever-changing sands of the AI acceleration.

Looking back over the last year, a ton has changed. About this time last year I left a lead frontend engineering role, then slowly skilled up until I could ship my own projects as a solo full-stack technical founder. Now I am in some newer, less settled role: a solo agentic founder operating an increasingly autonomous engineering and product system.

The transition has been shocking and exhilarating, but there is also a real feeling of loss in watching a lot of the skills and ways I used to work become less important.

Luckily, I was never too deeply attached to the physical act of writing code. I cared about the outcome and the reason for building it, and always loved seeing those performance gains (more of that to come), but I wouldn't say it was the defining factor of my existence.

So, sorry to break it to you, but if most of your identity came from writing immaculate React components or feeling like a god because you perfectly tuned a cubic-bezier, this transition is probably going to suck. Bummer. You, like me, still have to deal with it.

But the good news is nothing makes you feel more superhuman (and utterly powerless) than watching an agent and its subagent fleet finish in twenty minutes what 2024-you would have called a two-week feature.

A recent example was DiveJourney’s shop onboarding and first-event activation flow. The agents built localized onboarding, a persistent shop checklist, authoritative completion state, a linked-spots map, lifecycle email coverage, and a path that moves an operator toward publishing the first real event for their business and the analytics tracking and dashboards to do it. This would have taken a couple of weeks to lock in, and forget about how long it would have taken design and content and product to all align on the final approved version, put it in a sprint, ship it, QA rounds, and release flows.

You just accept that two weeks of work can become a 30-minute fleet push followed by another 45 minutes of walkthroughs and focused improvements.

That's just the job now, and I'll take an hour and 15 minutes over two weeks any day.

What engineering looks like when I do not write the implementation

For me, the engineering side of product building has changed into three main responsibilities.

1. Spec the right thing

The quality coming out of the agents can be extremely high. But that does not matter if the feature is wrong, the customer fit for the product is weak, or the spec ignores how the current system works.

The work begins with a solid spec: what problem this solves, how it fits the product, which users it affects, what existing systems it touches, what should intentionally not change, what evidence will count as done, and most importantly - why it even needs to exist.

The agent can implement the specification. It cannot perform an act of magic and prevent you from building the wrong product.

2. Connect the right context

The second job is building the context system that gives your coding agents all that sweet sweet context they crave without blowing through your context window.

That means the right repository instructions, plans, skills, plugins, MCP servers, source documents, issue history, third-party documentation, acceptance checks, and code surfaces - without blindly dumping the entire company into it and lighting tokens on fire.

All this to reach the goal of making the important context obvious and the irrelevant context easy to ignore by default.

3. QA the product

I do more QA now than I ever did when I wrote all the code myself. Part of that is obvious - I used to have a QA team whose entire job was to do this all day. But the bigger difference is that, when I wrote the code, I was testing it constantly just by building it. I knew how everything worked, where it felt weird, and what needed finessing because I had been interacting with every part of it the entire time. Now I am heavily involved in the spec, then I mostly disappear until a feature or checkpoint is complete. When I jump back in, I am seeing the completed output for the first time, without all the code and product context I used to naturally get while building it.

So now a lot of my engineering time is spent using the product, trying the edge cases, checking whether the flow feels coherent, and attempting to break it.

A recent DiveJourney shop onboarding flow is a good example. All the focused tests and checks were green, then I used it and found completion and skip states that still made no sense for the type of shop. A freedive operator could be pushed toward scuba-specific work, and the UX still didn't feel polished.

I continue to automate more of this. But as of now, nothing truly replaces opening the product and asking: does this make sense, feel good to use, and work when I try to break it?

Underneath all three responsibilities is a fourth, and admittedly one of the most fun: maintaining the machine the agents work inside and making that bad boy as clean, fast, and efficient as possible.

With agents doing most of the building, every architecture, system, and abstraction becomes part of how they think about and understand the codebase. The cleaner, more deterministic, and verifiable the system is, the easier it is for them to navigate, write the right code, and understand the bigger picture of how it all fits together.

That means I want:

  • a ton of fast tests that cover the right contracts and can run constantly
  • linting, formatting, and type checks that are effectively free, because agents hit them nonstop
  • strong types wherever they remove ambiguity and stop agents from wasting context trying to discover what reality is
  • E2E tests that do not make me hate my life when I see the monthly GitHub Actions bill
  • a harness that forces these guys to run all of the above at every point where they could have messed something up
  • architecture and abstractions that make the correct path obvious and repetitive slop harder to generate
  • bounded changes, rollback paths, and enough evidence to reject a bad direction without having to unpick some giant branch
  • enough structure that the agent can determine when we are actually done and passing without inventing the answer or running forever in an endless `/goal` loop

Basically, my engineering maintenance job is to make my agents as effective as possible so I can rapidly ship useful features and upgrades without wasting context, burning needless tokens, generating a billion lines of slop, or destroying production.

And as a quick aside, this is not what I would call vibe coding.

Vibe coding is asking a model for a thing, watching something appear, and calling it engineering.

What I am describing is asking the model for the thing, then forcing the output through a gauntlet of requirements, guardrails, tests, types, repeatable measurements, release gates, browser checks, rollback boundaries, and founder QA before I call it done.

Agents made me test more, not less

You can't trust an agent to get every decision right on the first pass or the second or the third. You also can't trust today’s change not to regress something it implemented three months ago. Agents are very good at writing tests, but every new test becomes another test the system has to run - often multiple times inside a single task.

For example, at the closeout of the first Bun migration, DiveJourney’s required non-Playwright test surface included:

Test lane
Test lanePassing tests
Pure tests 1,126
No-mock DOM tests 148
Grouped DOM-isolated tests 650
Route/mock-heavy isolated tests 695
Total 2,619

That's a solid amount of tests to wait around for - you might tolerate a two-minute lint or typecheck command a few times a day but an agent can hit the same command after the first implementation, before every commit, after a correction, after a review comment, after a release-base merge, after a regression fix, and again before the PR is allowed to move. And then that compounds when you have, say, 8 orchestrator agents delegating their tasks to 3 to 40 subagents, all of them doing their own thing and kicking off tests non-stop. That adds up.

A slow command is not one delay. It multiplies across every agent, every worktree, every review round, and every commit.

So the solution becomes to make those tests as fast as agentically possible so your agents stay on track doing their duty and so you don't check in on your fleet after four hours and realize they're only halfway through the spec because your tests take 30 minutes per round and now they're revolting from boredom.

The first major unlock was moving to Bun

This process actually started a few months before the latest modernization sprint.

DiveJourney was still centered on Node, pnpm, Vitest, jsdom, broad deployed Playwright gates, and CI lanes that had become too slow and expensive for the amount of work moving through them. I decided to test Bun against the actual project rather than read another benchmark post and daydream about how much faster my life could be.

The early spikes were promising, so I migrated the package manager, runtime, scripts, and required non-Playwright test lanes to Bun. It was much faster to migrate than I expected. Had I known, I would have done it months earlier.

Side note: whenever you ask an agent to estimate the effort for a migration like this, divide its answer by three. For some reason, SOTA agents seem to have a serious lack of confidence in their own abilities.

Anyway, the initial migration went so quickly that I decided to replace jsdom with happy-dom where it fit, move the required tests off Vitest and onto Bun’s native runner, and right-size the routine CI workers in a classic scope-creep move.

Bun was the catalyst, but the massive result came from combining the migration with those CI architecture changes. Together, they let me drop the runner size while making the whole thing much faster. Cheaper and faster. Win-win.

Comparable staging CI/CD metric
MetricBeforeAfterChange
Workflow wall clock 23m19s 4m23s 81% faster
Visible runner time 25m00s 4m45s 81% less
Approx. compute 98.8 vCPU-min 9.5 vCPU-min 90% lower

Again, that was not Bun alone. It was Bun plus the surrounding CI changes. But the end result was still massive.

Several individual lanes also changed substantially:

Lane
LaneBeforeAfterChange
Staging build step 82s 43s 48% faster
Routine deploy verification 16m56s 31s 97% faster
React/RTL DOM slice 16.85s 2.31-2.48s about 85% faster
Local migration tests 3m28.60s 22.14s 89% faster
CI migration-test step 2m26s 7s 95% faster
Warm/no-op install 1.52s 0.22s 86% faster
Fresh install 16.49s 12.86s 22% faster

That changed the way the agents could work.

Tests could run at nearly every stage. Regressions were caught closer to the change that caused them. PRs arrived in a more trustworthy state, and I spent less time manually finding obvious failures, writing them up, and sending them back through another repair pass.

Bun fixed one layer. Then I went after the rest of the toolchain.

The Bun work made the feedback loop much better. It also made me notice how much other improvement was still sitting there untouched, so I decided to run a tooling sprint and see how much more speed I could squeeze out of the agent loop.

After some research, I decided to try Oxc for linting and formatting, which would let me fully remove ESLint and Prettier. I also finally upgraded to TypeScript 7, which I had somehow still not done.

This was not one giant upgrade grenade. I broke the final modernization into a 31-PR merge map. Each slice had an owner, a bounded change, evidence, review, and a rollback path.

The integrated state included:

  • Oxlint and Oxfmt replacing ESLint and Prettier
  • globally enforced type-aware Oxlint rules
  • TypeScript 7.0.2 as the only direct compiler, which is just beautifully fast
  • Upgrade to Next 16.3 preview, React and Playwrite

The measured result

For the main toolchain comparison, I ran five isolated Linux samples against the exact baseline and integrated source states.

Check
CheckBeforeAfterChange
Lint 141.61s 0.39s 99.72% less time 🤯🤯🤯
Formatting 146.15s 19.50s 86.66% less time 🤯🤯
Cold typecheck 89.28s 14.24s 84.05% less time 🤯🤯
Warm typecheck 16.05s 4.25s 73.53% less time 🤯

The repository became smaller at the same time:

Repository inventory
Repository inventoryBeforeAfter
Direct dependencies 149 131
Lock package records 1,946 1,548
Unique lock names 1,382 1,174
Lockfile bytes 496,223 391,859
Package scripts 232 221

The lint number is almost absurd. A 141.61-second check became a 0.39-second check.

Lint went from being a full phase to being a half-second flash.

Cold typecheck went from a flow-breaking wait to a normal feedback step. Warm typecheck was less dramatic, but still substantial. Formatting is not instant at 19.50 seconds, but removing more than two minutes from a repository-wide check is still pretty insane.

And that is the result I actually care about. The agents can now lint, format, typecheck, and correct themselves constantly instead of burning minutes every time they need to find out whether they broke something. That means tighter feedback loops, less wasted context, and better features shipped faster.

A quick ShipFoundry aside

This whole process is also a big part of why I started building ShipFoundry.

I would absolutely tell another agentic builder to look at Bun, Oxc, and TypeScript 7 against their own repository. But “against their own repository” is the important part.

The stack is changing faster than I can manually evaluate it. Most updates do not matter to DiveJourney. A few can make it materially faster, cheaper, or easier for the agents to work in.

I do not need another changelog feed. I need something that checks an outside change against the actual product and codebase, decides whether it is worth acting on, and turns it into bounded work an agent can execute.

That is the problem behind ShipFoundry.

ShipFoundry did not create the modernization sprint in this post. But it has already found the next obvious place to investigate: whether parts of my Sharp image-processing setup can move to Bun.Image.

That is the kind of thing I want it doing: finding stack improvements that actually matter to my codebase instead of dumping another changelog on me.

The goal is that when the stack gets better, the relevant improvement can reach the product without me manually turning every release note into a week of investigation.

The job is different now

I do not need to type the code to own the engineering system.

I still have to decide what deserves to exist. I still have to structure the work, create the constraints, connect the context, define what success means, review the evidence, use the product, reject the wrong shortcuts, and decide what ships.

The product direction and encompassing system is now where I create most of the leverage.

The possibilities get ridiculous once that system is working well.

Rebuilding it made changes much cheaper to produce and verify but cranking out features in these autonomous loops isn't free to run.

Every new route, query, provider, cache decision, and background task still lands somewhere on the cloud bill underneath DiveJourney.

Which is the next part of this series.