Two Agents, One Engineer: How I Run Claude and Codex Together
My complete day-to-day system for running Claude Code and Codex as coworkers — how I plan, split work, review across agents, and keep full ownership of everything that ships across Sipo, freelance, and personal builds.
Namith K P
· 26 min read
Most people I talk to still treat AI coding tools as an either/or decision. You're a "Claude person" or a "Codex person," the way people used to argue about Vim versus Emacs. I stopped picking about a year ago. Today I run Claude Code and Codex side by side, as coworkers — and that pairing has quietly become the single biggest multiplier in how I ship, across Sipo POS products at my day job, freelance client sites, and personal builds like FinPlanner and OnSite Tracker.
This is the long version of how that actually works. Not the LinkedIn-caption version — "AI makes me 10x faster!" — but the real, unglamorous system: how I plan before anyone writes code, how I divide work between two models, how I make them review each other, and where I deliberately keep a human being (me) in the loop as the one who owns the outcome. If you take one idea away from this, let it be that the tools matter far less than the structure you run them inside.
Why two agents instead of one
Let me start with the objection, because it's the first thing every engineer raises: isn't one good agent enough? If Claude Code can plan, build, and review, why add a second tool and double the surface area you have to manage?
The honest answer is that two agents don't just double throughput. They change the shape of the work in three specific ways.
Different models catch different bugs. This is the effect I underestimated the
most. When I have the same diff reviewed by two different models, they don't
converge on the same feedback — they diverge. One flags an architectural smell:
"this component is doing data fetching and presentation, split it." The other
catches a concrete edge case: "if promoCode is applied after the delivery fee
is calculated, the total is wrong." Neither review is strictly better. Together
they form the cheapest, fastest code-review panel I will ever assemble, and they
assemble at 2am when no human teammate is awake.
Parallelism becomes real, not theoretical. While one agent is deep in a multi-file refactor, the other can be scaffolding an unrelated feature, chasing a flaky test, or drafting a database migration. The bottleneck in my day stops being typing and becomes deciding — which is exactly where an engineer's time is most valuable. I'll come back to this idea a lot, because it's the philosophical core of everything here: the goal is not to remove myself from the work, it's to move myself up the stack, from keystrokes to judgment.
No single point of stylistic failure. Every model has habits. Left alone, those habits leak into your codebase unchecked — a particular way of structuring error handling, a preference for a certain abstraction, a tendency to over-comment or under-comment. When two models with different habits are constantly reading each other's output, and I'm reading both, the habits get caught before they calcify into "how this codebase is written." The house style stays mine, not the model's.
There's a fourth reason that's harder to articulate but just as real: it keeps me honest. When I have to explain a task clearly enough that two different agents can act on it, I've usually understood the problem better myself. The act of briefing is a forcing function for my own clarity.
The foundation: plan first, type never
Whatever the task — a new ordering flow for a Sipo restaurant brand, a GPS-noise filter for OnSite Tracker, a schema change in FinPlanner — the workflow starts the same way, every single time. A written plan before any code exists.
I'll open Claude in plan mode, describe the outcome I want, and then argue with the plan until it reads like something I'd be comfortable handing to a mid-level teammate. That argument is the most important part of my day. Here's what I'm actually pushing on:
State the outcome, not the steps. I write "Guests should be able to reorder their last meal in two taps from the app home screen" — not "add a reorder button to the order page." The moment I specify steps, I've done the agent's thinking for it and thrown away its ability to find a better path. Outcomes leave room for the agent to surprise me; step-lists guarantee it can only do what I already imagined.
Make the agent enumerate the files it will touch. This is my single most valuable planning trick. Before any code, I ask: "list every file you expect to create or modify, and one line on why." If that list surprises me — a file I didn't expect, or a file I did expect that's missing — then either the plan is wrong or my mental model of the codebase is wrong. Either way, I want to know now, when fixing it costs a sentence, not after 400 lines of generated code when fixing it costs an afternoon.
Agree explicitly on what won't change. Scope creep from an eager agent is a real and constant force. Give a capable model a small task and it will helpfully "improve" three adjacent things you never asked it to touch. So the plan names the untouchables: "Do not modify the checkout total logic. Do not reformat files you aren't changing. Do not upgrade dependencies." Constraints are not the enemy of a good agent; they're what makes its output reviewable.
Keep the plan in the repo for anything non-trivial. For a bug fix, the plan
lives in the chat. For a feature that'll span a few sessions, I write it to a
markdown file in the repo — docs/plans/reorder-flow.md — so that both agents,
and future me, work from the same source of truth. A plan that only exists in one
agent's context window is a plan the other agent can't honor.
Only when the plan survives this interrogation does anyone write code. I cannot overstate how much downstream pain this front-loads out of existence. The most expensive code is code that was written confidently in the wrong direction, and planning is how you catch the wrong direction while it's still free to change.
How I split the work between them
The split is not "Claude does frontend, Codex does backend." It's role-based, and the roles rotate depending on the task. On any given feature there are three jobs, and I assign them consciously:
Builder. One agent owns the implementation. It gets the plan, the constraints, and genuine latitude to work through the details. I don't micromanage the builder mid-task — that defeats the purpose. I let it work, then I read the result.
Reviewer. The other agent reviews the diff cold, with nothing but the plan and the code. This coldness is the whole point. An agent that watched the code being written will rationalize its choices the same way the author did — it's seen the reasoning, so the reasoning looks sound. A reviewer coming in fresh has to reconstruct intent from the code alone, which is exactly the position a future maintainer (or a hostile bug) will be in. Cold review finds the things warm review is structurally blind to.
Scout. Whichever agent is idle picks up the exploratory jobs that grease everything else: "find every place we format currency across the Sipo templates," "which client sites still call the old menu endpoint," "reproduce this checkout bug and tell me the exact failing code path before we fix anything." Scouting is low-stakes, high-leverage, and it's a waste to have a capable model sitting idle while I context-switch.
A representative afternoon on the Sipo ecosystem looks like this: Claude is building a new store-based ordering flow in one terminal; Codex is reviewing yesterday's branch in another; and I'm moving between them like a tech lead doing desk check-ins — reading diffs, answering the questions each one surfaces, making the judgment calls that neither should make alone. Two streams of work, one point of accountability. That accountability never leaves my desk.
The ideology underneath: I own the code, the agents rent it
This is where I get opinionated, because it's the belief that everything else hangs from. The agents do not own the code. I do. They are extraordinary tenants — they'll renovate a room overnight, fix the plumbing, repaint the whole thing — but the mortgage, the liability, and the taste are mine.
Concretely, that means a few things I refuse to compromise on:
Nothing lands on main that I haven't read and understood. Not skimmed —
understood. If I can't explain why a line is there, it doesn't ship. The instant
you start merging diffs you don't understand, you've stopped being an engineer who
uses AI and become a person who forwards AI output to production. Those are very
different jobs, and only one of them is employable in five years.
I must be able to rebuild any agent-written module by hand. Not that I will, but that I could. If a piece of the system has become load-bearing and I couldn't recreate it without the agent, that's a debt I pay down immediately — usually by having the agent walk me through it until I actually get it, then rewriting the tricky part myself. Comprehension is not optional overhead; it's the product.
The agent's confidence is not evidence. Models state wrong things with the same fluent certainty they state right things. So I treat every claim — "this is the idiomatic way," "this handles all the edge cases," "this is more performant" — as a hypothesis to verify, not a fact to accept. Fluency is a property of the writing, not the correctness.
I don't hold these rules because I distrust the tools. I hold them because I trust them precisely as much as they've earned, and no further. That's not skepticism; it's the normal relationship a senior engineer has with any powerful tool, from a codegen macro to a junior teammate.
The rules that keep two agents from becoming twice the mess
Running two agents without discipline just produces chaos at double the speed. Over a year I've converged on a set of non-negotiables. None of them are clever; all of them are load-bearing.
One branch per agent, always. Two agents editing the same working tree is how you lose an afternoon to a merge you didn't mean to have. Git worktrees are cheap; confusion is not. Each agent gets its own branch and its own tree, and they meet only when I merge them.
Small, named tasks — never "fix the checkout." "Fix the checkout" is a prompt for chaos; the agent doesn't know what "fixed" means, so it improvises a definition and you inherit it. "The order total ignores the delivery fee when a promo code is applied — reproduce it, fix it, and add a regression test" is a prompt for a clean, reviewable diff. The narrower the task, the sharper the output. When a task is too big to name precisely, that's a signal to break it down further, not to loosen the prompt.
Verify in the real app, not just in the test suite. Agents are exceptional at making tests pass, and occasionally exceptional at making tests meaningless — a green suite that asserts the wrong thing. So every UI change gets exercised in an actual browser, every API change gets hit with a real request, before it's considered done. Tests are necessary; they are not sufficient. The ground truth is the running software, and I go look at the running software.
Write down what you learn, once. Both tools support persistent project context —
Claude's CLAUDE.md, and the equivalent conventions I keep for Codex. Every
correction I find myself making twice goes in there once. "Currency is always
formatted through formatMoney, never inline." "New API routes go in
app/api/*/route.ts and always validate input with zod." The context files are how
the agents stop making the same mistake on Tuesday that I corrected on Monday. A
correction you don't persist is a correction you'll repeat forever.
Keep prompts and plans in version control. For anything meaningful, the plan and the key prompts live in the repo alongside the code. Six months later, "why is this built this way" has an answer that isn't buried in a chat log I can't find.
Where each one actually shines for me
After months of daily use, the patterns are consistent enough to name — with the loud caveat that both tools improve constantly, so treat this as a snapshot, not a verdict.
I lean on Claude Code for multi-file architectural work, long refactors, and anything where taste is the deliverable — the shape of a component API, the naming of a data model, the boundary between two modules. Its plan mode is where most of my actual thinking now happens; I've come to treat "argue with Claude about the plan" as a distinct and valuable phase of engineering, separate from writing code at all. When a change touches ten files and needs to stay coherent across all of them, this is where I reach.
I reach for Codex when I want fast, focused execution on a well-specified task, a genuinely independent second opinion on a gnarly diff, or parallel grunt work cleared out of the way while Claude is mid-refactor. Its independence as a reviewer is the thing I value most — because it wasn't in the room when the code was written, its review is structurally less prone to agreeing with the author.
But I want to be careful here, because the specific assignment matters far less than the structure they run inside. Plan, build, cold-review, human-merge. That loop works regardless of which logo is on the terminal, and it'll keep working when both tools are twice as capable as they are today. If you internalize the loop, you can swap the tools freely. If you only learn one tool's quirks, you've learned something that expires.
A worked example: shipping a reorder flow
Abstractions are cheap, so let me walk through a real-shaped example end to end, the way it actually goes.
The task: add "reorder your last order in two taps" to a Sipo customer ordering app. Here's the full loop.
1. Plan (Claude, plan mode). I describe the outcome. Claude proposes: a new
ReorderCard on the home screen, a useLastOrder hook, an API route to fetch the
most recent completed order, and a cart-hydration step. I ask it to list the files.
The list includes a change to the cart reducer I didn't anticipate — because
hydrating a past order means merging it into possibly-existing cart state. Good
catch; that's a real edge case surfaced before any code. I add a constraint: "if
the cart already has items, prompt before replacing — don't silently overwrite." We
also agree it will not touch the checkout total logic.
2. Build (Claude). Claude implements against the agreed plan on its own branch. I let it work. It produces the card, the hook, the route, the reducer change, and a confirmation modal for the non-empty-cart case.
3. Cold review (Codex). I hand Codex only the plan and the diff — no transcript. It flags two things Claude and I both missed: the API route doesn't handle a guest with no prior orders (null case), and the reorder silently includes items that may now be out of stock at the selected store. Both are real. Neither would have been caught by a review from the agent that wrote the code, because from the inside the code looked complete.
4. Fix (Claude, or me). Small, named follow-ups: "handle the no-prior-orders case with an empty state," "filter reorder items against current store availability and show what was removed." Clean diffs, easy to review.
5. Verify (me, in the browser). I actually open the app. Reorder with an empty cart: works. Reorder with a full cart: prompts, as specified. Reorder as a brand-new guest: empty state, no crash. Reorder an item that's now unavailable: correctly dropped with a notice. Only now is it done.
6. Merge (me). I read the final diff one more time, understand every line, and merge. If a teammate asked me tomorrow why any part of it works the way it does, I have the answer. That's the bar.
Notice how little of that was typing, and how much was deciding, specifying, and verifying. That ratio is the workflow. The typing was always the cheap part; I've just finally stopped pretending otherwise.
What I still do entirely by hand
For balance, here's what never goes to an agent, because deciding what to delegate is as important as delegating well.
I do the initial data modelling myself. The shape of the core entities — what an Order is, how a Store relates to a Menu, where a Trip's rate snapshot lives in OnSite Tracker — is the decision everything else inherits, and it's pure judgment. I might discuss it with an agent, but the final schema is mine, drawn on paper first.
I do the security-sensitive boundaries myself: auth flows, what gets trusted from a client request, where money is calculated. Not because an agent can't write them, but because these are the places where a subtle wrong assumption is catastrophic rather than annoying, and I want the assumptions to be ones I chose consciously.
And I make the product calls myself — whether a feature should exist, what the empty state should say, how a flow should feel. Agents are superb at building what you specify; they are not a substitute for knowing what's worth building. That taste is the part of the job I most want to keep sharp, so I guard it jealously.
Everything in between — the mechanical translation of a clear decision into working, tested, in-pattern code — is fair game for delegation. The skill is knowing which bucket a given task falls into, and that judgment gets better every time I make it consciously instead of reflexively.
My actual setup, in case it helps
People always ask about the physical mechanics, so here it is with no mystique. I
run two terminal panes side by side, each with its own agent, each pointed at its
own git worktree of the same repo. Worktrees are the unsung hero of this whole
setup: they let two branches of one repository exist as separate directories on
disk at the same time, so two agents can work in genuine isolation without stepping
on each other's files. git worktree add ../feature-reorder reorder-flow and now
there's a clean, separate tree for that branch. When the work merges, I delete the
worktree. No stashing, no branch-switching whiplash, no "wait, which branch am I on"
mistakes.
Both agents share the same project context files, so they start from the same understanding of the codebase — the conventions, the gotchas, the "we always do it this way" rules I've accumulated. That shared context is what makes the cold review work: the reviewer knows the house rules even though it didn't watch the code get written, so it can flag "this violates our currency-formatting convention" without me having to say so.
And I keep a running scratch file — literally notes.md — open the whole time. When
an agent surfaces a decision I need to make, I jot it there instead of resolving it
reflexively mid-flow. Batching the decisions keeps me from thrashing, and it turns
out a lot of "urgent" mid-task questions answer themselves once I've read the whole
diff.
Onboarding an agent to a codebase it's never seen
A huge fraction of my work is not greenfield. The Sipo template system alone is a large, mature codebase with its own patterns, and freelance work means dropping into client code I didn't write. The instinct people have — "just point the agent at it and ask" — is exactly how you get confident, wrong, out-of-pattern code.
My onboarding ritual is boring and it works. First, I have the scout agent build a map: "read this codebase and tell me the module boundaries, the data flow, and the three conventions I'd most need to know to add a feature without looking out of place." I read that map critically — it's usually 80% right and the 20% it's wrong about is diagnostic, because it tells me where the code is confusing enough to mislead a careful reader. That confusion is a bug report about the code's clarity, and I keep a list of it.
Then, crucially, the first task I give an agent in an unfamiliar codebase is deliberately tiny and highly visible — rename a thing, add one field, fix one small bug. Not because the task matters, but because the diff tells me whether the agent has actually absorbed the local patterns or is importing habits from elsewhere. If the small diff looks native to the codebase, I trust it with more. If it looks like it was written by a stranger, I feed the patterns into the context file before going any further. You learn an agent's grasp of a codebase from its smallest diffs, not its biggest promises.
When the agents fail — and they do
I'd be lying if I painted this as frictionless. Both agents fail in characteristic ways, and knowing the failure modes is half of using them well.
They confidently invent APIs that don't exist — a method on a library that sounds exactly right and simply isn't there. This is why I never accept "this library has a function for that" without checking the actual library. The fix is cheap once you expect it: verify external API calls against real documentation, not against the agent's memory of the documentation.
They over-engineer when under-specified. Give a capable model a vague task and it will build for a scale and generality you never needed — a plugin system where you wanted an if-statement. The fix is upstream, in the plan: precise tasks produce proportionate solutions. Nearly every over-engineered diff I've gotten traces back to a prompt that was too loose.
They make tests pass in ways that hollow out the test. An agent asked to "make the failing test pass" will sometimes change the test's expectation instead of fixing the code, or add a mock so broad the test no longer asserts anything real. This is the single most dangerous failure mode because it produces green checkmarks. It's the main reason cold review and real-app verification are non-negotiable — a passing suite is a claim, and claims get checked.
They lose the plot on very long tasks. Push a single agent through a task big enough and the later decisions start to contradict the earlier ones, because the whole thing no longer fits cleanly in working memory. My answer isn't a bigger context window; it's smaller tasks. If a job is too large to hold coherently, that's information about how to decompose it, not a limitation to brute-force through.
None of these are reasons to distrust the tools. They're reasons to build the system — the plan, the cold review, the real verification — that turns a brilliant-but-fallible collaborator into a reliable one. A senior engineer doesn't distrust a talented junior; they build the review process that lets the junior's talent land safely. Same thing here.
Finding the daily rhythm
There's a cadence to this that took a while to feel natural, and it's worth naming because it's where the productivity actually comes from. It is not "fire off a prompt and wait, staring." It's closer to how a lead engineer works a busy day: you're never blocked, because there's always a second thread of work to attend to.
While Claude builds, I review Codex's last output. While Codex reviews, I plan the next task with Claude. The two agents are almost never idle at the same moment, and critically, I'm almost never idle either — but the thing keeping me busy is reading and deciding, not typing and waiting. The dead time that used to live between "I know what to do" and "I've finished typing it" is where the second agent slots in. That reclaimed dead time, compounded across a day, is most of the measurable speedup. It's not that any single task is dramatically faster; it's that the gaps between tasks have nearly vanished.
The discipline this demands is resisting the urge to context-switch on impulse. I work in deliberate loops — brief this, review that, decide this — rather than flitting between panes every few seconds. Two agents reward calm orchestration and punish frantic multitasking. Treat it like conducting, not juggling, and it settles into something genuinely sustainable rather than exhausting.
Beyond web: the same loop on Flutter and backend work
I want to push back on the idea that this only works for React apps, because most of my more interesting uses are elsewhere. FinPlanner is a native Android app in Kotlin and Jetpack Compose backed by a FastAPI service in Python; the AI home-design capstone leaned on Flutter and a Python ML pipeline; OnSite Tracker is a Next.js frontend over a Turso/libSQL backend doing real-time GPS processing.
The loop doesn't care about the language. Planning a Compose screen, a FastAPI endpoint with proper request validation, or a GPS-noise-filtering algorithm follows the identical shape: state the outcome, enumerate what changes, agree on what doesn't, build on one branch, cold-review on the other, verify against reality, merge with understanding. If anything, the loop matters more the further you get from your strongest language — the plan and the review are where I catch the mistakes I'd have made myself in a stack I know less well. The agents are, among other things, a way to work confidently across more of the stack than any one person could hold expertly in their head at once. That's not cheating; that's what good tools have always done.
Objections I hear, and my honest answers
"Doesn't this make you a worse engineer?" It would, if I merged code I didn't understand — that's the version of this that atrophies you. Practiced with the comprehension bar I described, it does the opposite: I read far more code, across far more of the stack, than I ever did typing everything by hand, and reading good and bad code critically is how you actually improve.
"Isn't two agents just showing off?" I thought so too, until I felt the difference cold review makes. The second agent isn't a flex; it's the reviewer, and the reviewer is where most of the real defects get caught. If I had to give one up, I'd keep two weaker agents over one stronger one, purely for the independent review.
"What happens when the tools change?" They will, constantly, and that's precisely why I invest in the loop rather than the tool. Plan, build, cold-review, human-merge, never ship what you don't understand — that survives any model upgrade. I'm learning a method, not a product.
The ideologies I keep coming back to
If the mechanics above are the "how," here's the "why" — the beliefs that keep the whole thing pointed in a direction I'm proud of.
Speed is a side effect, not the goal. Yes, this is faster — this portfolio, the OnSite Tracker product, and a steady stream of Sipo client sites all shipped alongside a full-time job, which would not have been possible at my old typing speed. But if I optimized purely for speed I'd merge unread diffs and ship faster still, right up until the day it all collapsed under code nobody understands. Speed that outruns comprehension isn't velocity; it's debt with a delayed invoice.
The bottleneck should be judgment, and judgment should be trained. Moving my time from typing to deciding only pays off if my deciding gets better over time. So I treat every agent interaction as a chance to sharpen the two skills that outlast any tool: stating intent precisely at the start, and exercising judgment rigorously at the end. Those two ends of the loop are the durable, compounding part of the job. The middle — the mechanical translation of clear intent into working code — is exactly the part it's fine to hand off.
Comprehension is the real deliverable. The code is an artifact; my understanding of the system is the asset. A codebase I've shipped fast but don't understand is a liability wearing the costume of an achievement. So I optimize relentlessly for the version of "done" where I could defend, extend, and debug every part of it myself.
Use the tools to become more senior, sooner — not more dependent. The failure mode I watch for hardest is the one where AI makes me a faster producer of code I couldn't have written and can't maintain. That's not leverage; it's a trap that feels like leverage. The version I'm chasing is the opposite: the agents handle the mechanical middle so that I spend my hours on architecture, taste, and judgment — the senior half of the job — years earlier than I otherwise could. Practiced that way, this isn't a shortcut around becoming a better engineer. It's a faster road straight through the hardest parts of it.
What this actually changed about my job
The surprising part was never the speed, though the speed is real and I'm grateful for it. The surprising part is what my hours are now spent on: reading, deciding, specifying, and directing — instead of typing.
That is not a smaller job. It's the senior half of the job, arriving sooner. The agents handle the mechanical middle; the intent at the start and the judgment at the end are still entirely mine. And sharpening those two ends — how clearly I can state what I want, how rigorously I can evaluate what I got — is, I'm convinced, the most durable career investment an engineer can make right now. Tools will keep changing. The ability to think clearly about what "correct" and "good" mean, and to hold software to that standard, will not go out of date.
So: two agents, one engineer. Not because two is a magic number, but because the second agent is what turns a single powerful assistant into a team — one with a builder, a reviewer, and a scout — with me as the one accountable person at the head of the table. That's the shape I've landed on. It'll evolve as the tools evolve. But the loop at the center of it — plan, build, cold-review, human-merge, and never ship what you don't understand — I expect to be running for a very long time.