# AI agent wrote your code? What you still need to know: a quadratic bug, measured

> When an agent writes the code, the owner does not need to read every line, but does need fundamentals to recognize something off, breadth to know where to look, and willingness to dig in. I ran Claude seven times: six outputs were linear, one fell back to a list scan, and doubling the input made it about 4x slower. A 7-point checklist is included.

- URL: https://zero2.perhapxin.com/en/a/agent-code-ownership-three-pillars
- Published: 2026-10-28
- Updated: 2026-10-28
- Language: en

# Agent-written code: you don't have to read every line, but you do need 3 kinds of background (a quadratic bug, measured)

> Short version: once an AI writes your code, the owner doesn't need to read every line. The owner does need enough fundamentals to recognize something off, enough breadth to know where to look, and the willingness to dig in when it matters. This post uses a 165-point Hacker News essay plus a function I ran myself to show what the first pillar looks like in practice.

I asked Claude to write a function that merges two lists and drops duplicates. Across four small tasks I got seven outputs. Six were textbook solutions. One hid a branch whose running time grows with the square of the input. With integer elements, a 16,000-item list takes 3 ms, so nothing looks wrong. Switch the elements to lists, keep the same count, and it takes 4.7 seconds.

If your acceptance bar is "the tests pass" and the test data happens to be integers, that function ships. This post is about that gap. Agents now write more code than you can read. How much do you still need to know to say you own the system?

## What the essay says

The essay is "Here be monsters: What do you still need to know when agents do the work?" on blog.adrq.dev. It was discussed on Hacker News on Oct 27, 2026, and I checked it on Oct 28 when it stood at 165 points. The post doesn't list an author or a publication date. I also couldn't fetch the page directly (Cloudflare blocked it), so what follows is based on a web-extraction summary with a few short quotes. Treat the quotes as approximate and check the original.

It starts with a failed CI run. The author's App.tsx was blocked by SonarQube on cyclomatic complexity. The file Codex wrote is about 30 lines, and 15 of those lines are an entire component on a single line. The essay gives no complexity score and no threshold. The author formatted the file, read it, and decided the complexity was justified by what the app does. Then they told Codex to "make SonarQube happy". Codex split the code into 10 files and CI went green. (The detail about the 10 files comes from the Hacker News commenter guitarlimeo, who quoted the essay.)

From this the author argues that ownership used to come from writing and debugging the code yourself, and agents cut that path off. They reject reading every line, and they reject not looking at all. In between, they say an owner needs three pillars:

1. Fundamentals: data structures and algorithms, networking, concurrency, distributed systems. This is what lets you recognize the shape of a failure, such as quadratic slowdown.
2. Breadth: just enough knowledge across areas to know what to look up when something is off. The essay compares it to buoys that guide your exploration of the ocean.
3. Just-in-time depth: the ability and willingness to dive into an unfamiliar problem and figure it out, plus the self-awareness to know when to ask for help.

The other case the author gives is their own orchestrator. Agents and subagents kept having stability and lifecycle bugs. Then they read a book on Kubernetes, picked up the idea of a level-triggered state machine, redesigned the architecture from scratch, and say it has been solid since. The essay doesn't name the book.

## The SonarQube example is weaker than it sounds

I want to say this up front because someone on Hacker News already did. The commenter guitarlimeo pointed out that the author talks about digging deep, but what they actually did was ask an AI to split the code into 10 files. That's not very deep. One sentence, "make SonarQube happy", solved it.

That criticism holds up. In the SonarQube case the author decided the complexity was justified and the warning probably acceptable, and the fix followed the tool. It demonstrates not letting a warning slide. It doesn't yet demonstrate understanding why. The two other cases carry the argument better: the quadratic time curve and the orchestrator redesign.

## Pillar one: what quadratic looks like

The essay's quadratic example uses a diving robot. At 2 meters deep the trip back takes 4 seconds, at 4 meters it takes 16, and at 16 meters it takes 256. The first step doubles the depth and quadruples the time. The 16-meter point is the square of the depth. You don't need the formal definition of Big-O. You need the rhythm: input times 2, time times 4, and that's your cue to go looking for a nested loop hiding somewhere.

The essay only offers the metaphor. I wanted to know whether code from Claude actually looks like that, so I ran a small experiment.

### Setup

On Oct 28, 2026 I ran `claude -p` in an empty folder with the system prompt `You are Claude, a helpful AI assistant.`, no tools, and no settings loaded. I wrote four everyday tasks that engineers meet every week: dedupe records by id and keep the latest, find duplicate strings, attach orders to users, and merge two lists without duplicates. Three of the tasks ran once each on Haiku and Sonnet. The fourth ran once on Sonnet only. That makes seven outputs. The prompts and raw outputs are in the project's runs folder.

The result surprised me. Six of the seven used a set or a dict and made a single pass over the data, so they run in linear time. For attaching orders to users, both models grouped orders by user_id first and then looked each user up. Nobody wrote a nested loop.

### The one exception

The odd one out was Sonnet's merge_unique. The task was a single sentence: merge lists a and b, return a list without duplicates, keep the order. It returned this:

```python
def merge_unique(a, b):
    seen = set()
    result = []
    for item in a + b:
        try:
            if item in seen:
                continue
            seen.add(item)
        except TypeError:
            # unhashable item: fall back to a linear comparison
            if item in result:
                continue
        result.append(item)
    return result
```

(The original comment was in Chinese, since I prompted in Chinese; I translated it.) The main path is fine. It even handles a case I never mentioned: if the elements are lists or dicts, which can't go into a set, it falls back to checking `item in result`. That's thoughtful. The cost is a hidden slow path, because `item in result` scans the whole result each time, and doing that n times is n squared.

I timed it with Python 3.11.17. Here n is the number of items in each of the two lists, with half of them overlapping:

| Element type | n | Time |
|---|---|---|
| int | 2,000 | 0.2 ms |
| int | 4,000 | 0.8 ms |
| int | 8,000 | 0.9 ms |
| int | 16,000 | 3.0 ms |
| list | 2,000 | 70.8 ms |
| list | 4,000 | 295.7 ms |
| list | 8,000 | 1,168.4 ms |
| list | 16,000 | 4,714.4 ms |

Double the count and the time is about 4 times larger (my measurement). Going from 70.8 to 295.7 is 4.2x, from 295.7 to 1,168.4 is 3.95x, and from there to 4,714.4 is 4.03x. That's the essay's 4-seconds, 16-seconds curve, shrunk down to milliseconds.

The limits first. This is one machine and a single measurement per size, so the absolute numbers mean little. I only used lists as the unhashable element, and I ran each task once or twice. I didn't test Opus and I didn't rephrase the prompts and rerun, so I can't say whether a different wording sends more outputs into the fallback branch. The conclusion this experiment supports is narrow: Claude's usual output is mostly linear, but when it adds a fallback for an edge case and that fallback is brute-force comparison, quadratic time can hide in it, and a test suite that only uses hashable data won't see it.

### After finding it: hand the measurement back to the agent

Once I had the numbers, I didn't rewrite the function. I gave the numbers and the function back to the agent. I used Sonnet again, pasted the code and the four timings, and asked why it was slow and how to fix it while still supporting unhashable elements. The answer was that lists can't go into a set, so every element falls into the fallback branch and scans result linearly, which makes the whole thing O(n²). It proposed recursively converting lists, dicts, and sets into hashable keys for deduplication, and only using linear comparison for custom unhashable objects.

I measured its new version on the same data. With list elements and 2,000, 4,000, 8,000, and 16,000 items per list, the times were 3.6, 6.4, 13.4, and 30.6 ms. At 16,000 items that's down from 4,714.4 ms to 30.6 ms, and the growth ratios are back near 2: 1.78, 2.09, 2.28. The last two run a bit above 2 and I didn't look into why.

The order mattered. Having the doubling pattern first is what made "why" a good question, and gave the agent numbers to check its answer against.

That round only verified timing, not behavior. The new version treats `[1, 2]` and `(1, 2)` as different, same as the original, which the agent stated in its explanation; I didn't add a test for it. If this were going to production, that step belongs to the owner: run a few edge-case inputs (an empty list, nested lists, a mixed list containing `1`, `1.0`, and `True`) through both versions and diff the outputs.

### How to spot it at a glance

This is my own reading habit, not something from the essay or from research:

1. An `in list`, `list.index`, or `list.remove` inside a loop is a hidden second loop.
2. A loop that calls a function that scans all the data, such as filtering the entire orders table on every iteration.
3. A fallback branch written for an unusual case. These rarely get tested, and they're the most likely slow path.
4. A string that keeps growing with `+` inside a loop. In some languages and situations each step copies the whole string.

None of these requires understanding the whole function. You scan for them, and when you see one you ask: if the input doubles, what happens here? That's how fundamentals get used in the AI era. You don't have to write the code. You have to recognize the shape of what someone else wrote.

## When a warning shows up: back to the SonarQube step

If the CI failure in the essay were replayed with the approach above, there would be a few extra moves. First, ask what the tool actually measures. Cyclomatic complexity counts independent branch paths in the code, not layout, so cramming a component onto one line affects readability but not the score. Which function had too many branches, the essay doesn't say, and I don't know. Second, ask whether the number means anything for you. The author read the code and found the complexity justified. That's a reasonable call, but the follow-up should be writing down the reason, not just making the warning disappear.

Only then comes the handling. Splitting into 10 files to get CI green is an acceptable outcome, since each component should be easier to read and test alone (that's my guess). But it isn't the same as lowering the complexity. I suspect the total number of branches didn't drop; it was spread across files. The essay gives no before-and-after scores, so I can't verify it. Whether the next person can understand the code faster is where the refactor pays off, and the essay doesn't measure that.

## Pillar two: breadth is knowing where to ask

Breadth is hard to demonstrate because it's invisible until something breaks. I think the most useful line in the essay is that breadth only has to be enough to know what to look up.

The orchestrator story shows it. The author hit a problem, read a Kubernetes book, and recognized the idea of level-triggered control there. You could also read that as going and learning on the spot, which belongs to the third pillar. My own reading is that recognizing the similarity while reading depends on already having some idea of state machines and control loops. The essay doesn't say that.

A few Hacker News comments added practical detail. smallerfish said LLMs make poor database schema normalization decisions, and on AI-heavy projects they stop feature work every few days to review the code and the schema. That's the same pattern as the quadratic bug: each small decision works, the damage accumulates, and a schema gets very hard to change once data depends on it. This is the commenter's own experience and I haven't verified it, but it points at the places worth a standing review.

Another came from mrothroc. After moving from individual engineer to leading a team, they couldn't read the team's code as deeply as code they'd written themselves. What they learned was to see a system as boxes and lines: components and how they interact. They ask the team to deliver sequence diagrams, so even if each box is opaque, the boundaries and the flow are clear. It's a version of the map you can start using tomorrow.

The opposing view belongs here too. ArcaneMoose said this high-level map can often be generated by an AI on the spot, so you don't need to know it all and can just ask. I agree that holds in many situations. But EduardLev's reply is closer to my experience: the context you need often spans several repos, config files, and a team's history of verbal decisions, none of which is in the code, so an AI may not be able to get at it.

## Pillar three: just-in-time depth, what to do when you're stuck

"Willing to dig in" sounds like an attitude problem. I think it breaks into three moves.

First, shrink the problem: turn the symptom into a minimal repeatable case. For the function above, three lines of benchmark reproduced the slowness. Second, before asking the agent, form a guess of your own, even a wrong one. Third, give the guess to the agent to check, instead of sending a bare "fix this".

There's a reason for that order. If you ask with nothing in mind, I suspect it's hard to judge whether the agent's answer is any good. If you have a guess, the agent's reply becomes a data point to compare against. The essay ends by saying future owners will be generalists who combine all three pillars, like kids who took radios apart and learned as they went. I agree with the direction, but that claim has no evidence behind it. It's the author's expectation.

If you're a junior engineer who hasn't hand-written much code, my view is that deliberate reading practice can substitute. Take some agent output, pick a piece you don't know well, and spend ten minutes asking "what happens if the input is 10 times bigger?" That's my own suggestion, not the essay's, and no research backs it. I just think it's the cheapest option.

## A checklist you can use today

Paste this into your PR template or into your instructions to the agent, and run through it every time you accept agent code.

1. Is there an `in list`, `index`, or `remove` in a loop, or a call to a scan-everything function inside a loop? If so, ask: if the input doubles, how many times slower does this get?
2. Is there a fallback branch for an edge case? Is its complexity higher than the main path's?
3. Write a very short benchmark. Measure at least three doubling values of n, repeat each a few times and take the minimum, and aim for runs of tens of milliseconds or more. Small-n noise misleads. My integer data went from 0.2 to 0.8 ms, which also happens to be 4x, and that code is linear. Only suspect quadratic when the ratio stays near 4:

```python
import timeit
for n in (4000, 8000, 16000):
    data = make_data(n)  # replace with your own data generator
    best = min(timeit.repeat(lambda: f(data), number=1, repeat=3))
    print(n, best)
```

4. For tool warnings (complexity, types, security), separate "why did this warning appear" from "how do I make it go away". Doing only the second is the same as not looking.
5. For hard-to-reverse decisions such as database schemas, public APIs, and data formats, don't rely on the agent alone. Schedule a standing review.
6. Can you describe the system as boxes and lines? If not, ask the agent to draw it, then check it against the code yourself.
7. When something breaks, the first question is whether this is a failure shape you recognize. If it isn't, that's your signal to dig in or find someone.

One Hacker News commenter (alabhyajindal) thought the essay itself reads like AI writing. I can't judge that.

This checklist has limits. For a small script whose data stays small, quadratic never triggers. For a one-off data conversion, nobody cares if it's a bit slow. For problems with no notion of data size, such as UI layout or copywriting, none of these checks apply. It fits growing data and long-lived services.

It also doesn't promise to catch everything. My experiment covered a small set of Python functions. I didn't touch concurrency, distributed systems, or front-end work, so for that half of the first pillar I have no data of my own.

## FAQ

**I'm not an engineer. Do I need to know this?**
If you're having an agent build a personal tool, no. If other people will use the system, data will pile up, or a failure will hurt someone, you're the owner, and you should at least be able to ask "what happens when the data is ten times bigger?"

**An AI can review the code too. Why do I need to understand it myself?**
It can. In the experiment above, one benchmark is what caught the problem. But I suspect that when a model of the same kind does the review, you still need to know what to ask. I didn't test that.

**How do I tell whether the AI wrote something bad or the tool is giving a false alarm?**
That's what the essay and the Hacker News comments argue about. My approach is to build the smallest reproduction first. If you can measure a difference, it's a real problem. If you can't, write down why and then decide whether to accept the warning.

---
Sources (Cloudflare blocked direct fetching of the original essay, so I worked from a web-extraction summary with item-by-item quotes rather than the full text; check quotes against the original):
- Here be monsters: What do you still need to know when agents do the work?, blog.adrq.dev, retrieved Oct 28, 2026
- Hacker News discussion https://news.ycombinator.com/item?id=50258868 (165 points, retrieved Oct 28, 2026)
- Test log: the runs folder (`claude -p`, Haiku and Sonnet, Python 3.11.17, Oct 28, 2026)

Test environment: one machine, one or two runs per task. Results are indicative only.


## FAQ

### Do I still need to read every line of agent-written code?

The essay author argues against reading every line and against not looking at all. He says owners need fundamentals, breadth and just-in-time depth to recognize anomalies and dig in.

### How can I spot quadratic slowdown quickly?

Double the input and watch for roughly 4x the time. In my test of a Claude-written function with list elements, 2,000 to 16,000 items took 70.8, 295.7, 1,168.4 and 4,714.4 ms.

### Does Claude often write slow code?

I ran four small tasks and got seven outputs: six were linear and one fell back to a linear scan in an edge-case branch. The sample is small and proves nothing general.

## Sources

- [Here be monsters (blog.adrq.dev, retrieved Oct 28, 2026)](https://blog.adrq.dev/here-be-monsters-what-do-you-still-need-to-know-when-agents-do-the-work)
- [Hacker News discussion](https://news.ycombinator.com/item?id=50258868)
- [Test log and benchmark (Oct 28, 2026)](/media/cnt_mvrmkls0vy3f/runs/tasks.json)
