I write roughly zero percent of the code in my products now. Welcome to the future.
Which isn't a fun way of saying I use Copilot. I mean I don't write the implementation at all anymore.
I still read through a section when something feels off, inspect an architectural decision, or debug something weird data-side. But outside of API keys and environment configuration, I am not sitting there typing code anymore.
This has all been relegated to my agent fleet - and one of the products where they fleet heavily is DiveJourney.
DiveJourney is the global platform for dive travel: with 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 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 and it's only become easier as the models and harnesses kept improving.
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 founder operating an increasingly autonomous engineering and product system.
The transition has been both 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 and or obsolete.
Luckily, I was never too deeply attached to the 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 at the same time) as 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 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 code
For me, the engineering side of product building has changed into three main areas.
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 but it can't magically prevent you from building the wrong feature.
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 with the the goal of making the important context accessible 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 work with 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 primarily 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.
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 correct implmentation, 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 don't make me hate my life when I see the monthly GitHub Actions bill
- a harness that forces them 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
- enough structure that the agent can determine when we are actually finished with the feature without 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 improvements without token waste, infinite lines of slop, or blowing up production.
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 | Passing 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 and an agent will hit the same commands multiple times - 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, etc.
And that compounds when you have, say, a number of orchestrator agents delegating their tasks to 3 to 20 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 worktree, agent, review round and commit.
So the solution becomes to make those tests as fast as possible so your agents stay efficient and 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.
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. I decided to test Bun against the 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, current SOTA models seem to have a serious lack of confidence in their own abilities.
Anyway, the initial migration went so quickly that I decided to fully jsdom with happy-domt, and go ahead and move all tests off Vitest and onto Bun’s native runner, which then let me right-size the CI workers. All in a classic scope-creep form.
So Bun was the catalyst, but the massive result came from combining the migration with those CI and test architecture changes. Together, they let me drop the runner size while making the whole thing much faster. Cheaper and faster. Win-win.
| Metric | Before | After | Change |
|---|---|---|---|
| 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 | Before | After | Change |
|---|---|---|---|
| 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 sooner. PRs arrived in a better state, and I spent less time manually finding obvious failures, writing them up, and sending them back through another repair pass.
Then I moved to the rest of the toolchain.
The Bun work made the feedback loop much better, but it also made me realize 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 dev loops.
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.
The integration plan 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 | Before | After | Change |
|---|---|---|---|
| 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 | Before | After |
|---|---|---|
| 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 ridiculous. 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 needed something that checks an outside change or update against the product and codebase, decides whether it is worth acting on, and turns it into work an agent can execute.
That is the idea 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 can improve my codebase without dumping another changelog on me.
The goal is that when your stack improves, you can absorb the relevant improvements without manually reviewing every release note and turning it 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 should be built. I still have to structure the work, create the constraints, connect the context, define what success means, review the evidence, use the product, and decide what ships.
The product direction and overarching system is now where I create most of the leverage - and the possibilities get ridiculous once that system is working well.
Rebuilding the tooling it made features much faster to build and verify but cranking out features in these autonomous loops isn't free.
Every new route, query, provider, cache decision, and background task still lands somewhere on the cloud bill underneath the product.
Which is the next part of this series.