What you don't see

Published: 01 Mar 2026

More and more, with the progress of coding agents, people are rewriting software.

And honestly, it looks easy. You write a good prompt. You get a plan. You execute the plan. One feature at a time (based on your Product Requirements Document/PRD). Ship.

Last week, Cloudflare showed vinext, an experiment to run the Next.js API surface on their own stack. One engineer, one week, about $1,100 in tokens. It is impressive. It is fast. It proves something important: with the right agent setup, you can rebuild a lot of software very quickly.

And that is exactly the problem.

Because when you rebuild mature software quickly, you rebuild what you can see. You do not rebuild what you cannot see.

Scars, Not Accidents

Mature software is not big because developers enjoy complexity. It is big because it has been through years of production traffic, weird integrations, security research, fuzzing, CVEs, bypasses of the bypasses, and painful post-mortems.

Over time, those incidents leave marks. A strange conditional branch. A strict parser that feels "too strict." A default that seems overly conservative. A normalization step that nobody dares to remove. These are not accidents. They are scars.

You do not notice them when you use the software. You only notice them when they are gone.

Security Does Not Live in the Happy Path

When someone rewrites a framework in a weekend, they start from the surface: routes, features, happy paths, compatibility for normal users.

But security does not live in the happy path.

Security lives in malformed inputs. In encoding edge cases. In parser differentials. In ambiguous specs. In "works as intended" gaps. In behavior that only shows up when two components disagree on what a string means.

Those things are not visible from the outside. They are the result of years of people trying to break the system.

When you rewrite something from scratch with or without an agent, you see the features. You do not see the battles that shaped them.

URL Parsing and vinext

This is not abstract. Consider URL parsing. The Node.js package url-parse was written as a lightweight alternative to existing parsers: small footprint, clean API, works in the browser. It looked like a better, simpler version of something mature. It got millions of downloads per week.

Then came the CVEs. Six of them in under two years. Backslashes treated as relative paths. Missing ports causing hostname confusion. Encoded characters slipping past validation. Each one a bypass that let attackers forge hostnames, redirect users, or reach internal services. Each one exploiting an edge case that the battle-tested parsers had already learned to handle, through years of exactly this kind of breakage.

The original parsers were not complicated because their authors liked complexity. They were complicated because http://example.com: and https:\ and http:///google.com all need to resolve to something, and the wrong answer is a security hole. That knowledge was not in any spec. It was earned through pain.

And then there is vinext itself. Within days of the announcement, Vercel's security team audited the project and responsibly disclosed seven vulnerabilities: two critical, two high, two medium, one low. Nothing like spite-auditing ;). The team at Hacktron went further: they pointed their autonomous scanner at vinext and came back with 24 validated findings, including session hijacking via race conditions, cache poisoning that served one user's authenticated data to everyone, and middleware bypass through double-encoded paths. Classic parser differentials between layers that each decoded the URL at different stages.

The most telling detail from the Hacktron report: Next.js has an undocumented heuristic where requests containing Authorization or Cookie headers automatically opt out of shared caching. That behavior is not in any API spec. It evolved from production incidents. When vinext reimplemented fetch caching from the spec, that invisible rule did not exist, so the first authenticated user's response got cached and served to every subsequent visitor.

That is what a scar looks like. It is not in the feature list. It is not in the docs. It is the result of someone getting burned, and the fix quietly preventing everyone else from getting burned too.

The Invisible Value

The uncomfortable truth is this: in mature software, a large part of the value is invisible.

It is the conservative default that prevents a foot-gun. It is the input validation that feels annoying. It is the strictness that breaks your "creative" use case. It is the check that seems redundant. It is all the times someone already tried to break it.

Agents are very good at producing output. They are not automatically good at reproducing history. And history is what hardens software.

How to Rewrite Responsibly

This is not an argument against rewrites. Sometimes the architecture really is broken. Sometimes you do need to start over. And now that coding agents have lowered the cost of rewriting to almost zero, more teams will try.

But if you are going to replace something mature, assume you are missing the most important parts.

Once the rewrite works, go through the CVEs that impacted the original. Not just the titles. Read the advisories. Understand the mechanisms. Look at what the mature codebase does that yours does not, and ask why. Find the checks that seem redundant, the defaults that seem too strict, the logic that looks like over-engineering. Figure out what is auto-magically preventing issues or enforcing security in the original, because odds are your clean new version quietly lost it.

Turn those into tests. Run them against your rewrite. See what breaks.

In my code review training, I recommend playing "spot the differences." Take two codebases and try to find what is done differently, then ask: is there a security impact? This strategy works perfectly here. Put the original and the rewrite side by side. Do not look at what is common. Look at what is different. That is where the vulnerabilities are.

Photo of Louis Nyffenegger
Written by Louis Nyffenegger
Founder and CEO @PentesterLab