The Great Multiplier

Agents multiply whatever is given to them, which is why knowing your codebase has never mattered more

Published

Like many software developers, I haven’t really written code for work since December 2025. And yet it’s never been more important that I know my codebases. Just in the past year we’ve gone from Large Language Models that could barely output a file’s worth of coherent code to agents that can build out medium size projects in an afternoon’s worth of prompting. These agents are multipliers; they take whatever you give them and dial it up to 11. A good mental model of your codebase gives you a junior engineer while a bad one, or none at all, turns it into a full-on nightmare.

How I Got Here

Early Days

I used to use web UIs to debug specific code snippets and re-work small files before using agents. So I wasn’t totally a noob. Copy-pasting snippets of code between my editor and the browser - practically prehistoric!

I started using agents probably in mid-to-late September 2025. I had a new-ish Google Cloud account that included credits for Gemini. So, Gemini CLI was what I started using as my first agentic app.

Initially I used agents for things like debugging component states in React, refactoring components, consolidating shared functionality across files, etc. Stuff where I knew exactly what was to be done but didn’t really want to do because frankly, they suck to do by hand.

Switch over to Claude Code

Switching over to Claude Code (CC) was a game-changer. In December 2025, a friend had a referral code and suddenly I had $10 worth of credits to use. Anthropic were in a stage where they were aggressively growing CC. Limits were generous and usage was discounted. Immediately, I felt a difference. The agent had the capability to make changes independently and I had the confidence to allow it to do so.

My prompts went from

Gemini, please take a look at @[FILE] and @[RELATED FILE]. [FEATURE] currently does [X], but I need it to do [Y]. Can you explain how it works and then make the change, while making sure [EXISTING BEHAVIOR] doesn’t break?

to

Okay, next change: [FEATURE] should do [Y] instead of [X]. Can you update it?

This is quite a change. I went from supervising the agent and giving it specific work orders to delegating work with the confidence it would get done correctly. This shows not only how much better the model was but also how much better a harness Claude Code was over Gemini CLI.

Interestingly in this transition, I went from reading and reviewing every single line of code to letting the agent do its thing. The code generated is fast and cheap but isn’t free. Our efforts shift from actually writing the code to ensuring that the code written/generated does what we actually want it to do.

The Flow Now

This flow below is for bigger feature changes. For smaller changes, I just yolo things now.

  1. Write out a basic spec document that lays out a high-level overview of what the features being implemented do, the main data structures and the operations and interfaces on them. This process takes a couple of days because I let ideas simmer. My direct input is highest here because I’m essentially transferring my understanding to a document to generate code.
  2. The Planner model gets this handwritten draft. I have the Planner critique it and find out the shortcomings in the logic. A main objective at this stage is to find out how well it fits in the general codebase. A working knowledge of the codebase helps tremendously here because you can tell when the model is bullshitting you immediately.
  3. A couple of rounds of back and forth with different Planners lead to a final spec and a plan that gets passed to Implementation models that build out code per the plan. These are typically Sonnet/GPT-Luna class models.
  4. I either a) write tests by hand or b) task another agent with writing tests to catch the Implementation model out. The option really depends on how important the feature being added is. Handwritten tests are important because they catch a difference between what I know the code should do, what the model thinks the code should do and what the model actually did. I’ve actually gone from minimal testing to actively thinking about tests to write.
  5. Once tests pass, I go through the code once before pushing it to ensure that things are generally implemented correctly. Pre-push checks need to be really strong here. If you miss things out or don’t test comprehensively you’ll probably end up with code that comes back to bite you in the ass.

What I Check (and What I Don’t)

Let’s start off with what I don’t bother to check much anymore. Models can pretty much get syntax right; linters and formatter can either be run by the model itself through a tool call or can be a part of the pre-push checklist. Anything else stylistically is a single line in an AGENTS.md file. Putting in other hooks to run items on a pre-push checklist ensures that nothing else gets by.

Now what I do pay attention to -

  • Divergence in what should be implemented and what actually gets implemented. Plans are broken down into Phases and Sections (the LLMs looove phases). The Implementation model veers off this plan usually once the complexity level crosses a threshold. Small additions and hyperfixations creep in. It’s extremely important to keep at it and ensure the models don’t mess around. Other times, the agents just do NOT implement something citing a tangentially related reason. I have to prod them to get the work done.
  • Logical errors that arise because instructions were not explicit or context was under-defined or implicit in its nature i.e. you think it’s common sense but the agent doesn’t even clock it.
  • References to sections and phases from the implementation plan in the source comments. Makes absolutely no sense even a week after push.
  • Growth of unnecessary stuff in the codebase. Agents sometimes do MORE than what is asked. A request to do a simple refactor of a conditional triggers multiple, unnecessary checks and tests. It’s easy for bad code practices to get baked into the codebase. Future runs treat them as gospel and pile on more of the same. Simply put, it’s the start of a slop loop. The mantra here is “Prune, baby, prune”.
  • The planning/design stage is really important. Reviewing the planned spec is crucial because a screwed up spec means a poor implementation. Worse this implementation gets written to the agent’s memory. So you have traces of horrible code styles/approaches creeping into sessions that are created weeks later.
  • Whenever an Implementation agent disagrees with the Planner agent, I manually check what it is attempting to do and rework the spec and the plan if required.

A key thing to note here is that all of these only work when you do understand the codebase well. Clarity on the feature functionality isn’t sufficient.

Code I Know vs Code I Don’t Know

After almost a year of writing code assisted by agents, I would say that there is a vast difference between working in a codebase I know and one I know nothing about.

I have absolutely no feel for the code in repos that are agent-generated. If tomorrow, there was a total ban on agents, I probably would take a few days to even understand what is happening in them. Of course, I know what the project itself is supposed to do but I probably would not be able to make even small changes simply because I have no idea what else I’d trip up. This is a direct result of letting the agent do its thing. And that’s totally fair because that’s the trade we’ve made.

Something that is missing from such repos is structure. Legacy handwritten codebases have a certain pattern and cadence to them. If you read them enough, you’ll probably be able to say who wrote what. This is totally missing from agent-generated repos. I’d venture that this is because the agents/harnesses miss a longer term mental model of the project/requirements.

Agent-generated codebases are quicker to get started with. For example, an HTTP service is a couple of minutes of work at most once you specify what models are needed. But longer term development and maintenance sucks because A) the human feel for the codebase doesn’t exist and so the human driving the agent can’t correct it and B) agents rely on memory (which isn’t great) and re-create mostly the same thing each session but not quite exactly.

Agents are much better at navigating and adding extensions to repos that have substantial handwritten code. I think it could be simply that these handwritten codebases have patterns which aid the agent to make changes. The developers also have a better understanding of such codebases allowing them to steer these agents better.

Close

If you’re stacking shit and ask the machine to help you out, you’re suddenly going to be staring at a 100-storey shit tower. And because the agent starts with a 100-storey shit tower next session, it very quickly compounds to a 1000-storey shit tower.

I’m by no means advocating that developers should sit and read the entire codebase. That ship has sailed. Generating code is so cheap that reading every single line of code honestly doesn’t matter outside of educational reasons. What is important however is developing a mental model of any repo that is sufficiently robust to steer these agents that are becoming an integral part of our workflow.

At the end of the day, agents are multipliers that don’t really care about what they’re multiplying. It’s up to the developer to decide what goes into the multiplier. The best way to prevent 100-storey shit towers is to have a great mental model of what you’re building.


Odds and Ends

A few things that don’t quite fit anywhere else.

Specs and Planning

  • A proliferation of .md spec files leads to conflicting signals on the direction of the codebase. Models (even SOTA Planners) get confused on the foundations of what they are doing. It’s important to continuously re-evaluate requirements to ensure that only the critical stuff stays.
  • Planner models are NOT infallible. The smaller, cheaper Implementation models catch them out occasionally. This is a function of context, memory and honestly the model itself.

Cheap Code

  • Flexibility is increased. Feature experiments are much faster. Code is plentiful and code is cheap. This means that features can be written out instead of debated in the abstract.
  • At this time, I’m not particularly sure why code re-use is not a priority anymore. DRY is a key tenet of mine. Maybe it’s just that it’s way simpler to get the models to re-generate an equivalent function instead of having the model re-use previously generated functions? Or is it an economic thing where token generation is so cheap that it quite literally doesn’t matter? I don’t know to be honest.

Harness and Memory

  • Harnesses are very important. I’ve tried out a few harnesses now - Gemini CLI (is it even a coding harness?), Claude Code, Codex (the TUI version - not desktop), OMP, Pi. I’ve used the same model (GPT-5.5/5.6 series) on different harnesses and anecdotally, the “first-party” harnesses for the models - Codex and Claude Code - vastly outperform the generic ones. They consume fewer tokens, have better memory layers, make more succinct changes etc. ymmv here.
  • A harness’s memory matters as much as the model itself. Claude Code typically gets what I mean pretty much all the time (GPT is a smidge behind). I think that this has more to do with how the harness manages memory than with the model’s inherent ability to understand me. Memory management i.e. selecting the right information required for the next response seems to help it turn my prompts into the changes I actually want.

Model Behaviour

Models have a tendency to pick on one single phrase or requirement and treat it as load-bearing (heh, see what I did there?). This is… irritating to say the least. Many times a simple “knock it off” is enough but occasionally a restart of the process is required.