Back to Lab

Claude Code vs Codex: The Split I Use to Ship Production Code

Stefan7 min read

Every day someone announces they are dropping Claude Code for Codex, or Codex for Claude Code. Both moves solve the wrong problem. The two tools are miscalibrated in opposite directions, and that is the reason to run them together rather than pick between them.

The split I have used for most of this year: interview and plan with Claude, review the plan with Codex, implement with Claude, verify against the plan with Codex. Claude handles anything conversational. Codex handles anything that requires someone to be pedantic.

The slides for this talk cover the same split in outline form.

How do Claude Code and Codex actually differ?

Both tools are wrong about the same thing, in opposite directions. One rushes. One is too careful. Once you see it that way, the division of labour picks itself.

Claude Code overlooks things

Claude, running on Opus, rushes. It has a bias toward getting to an answer, which used to be visible in the system prompt and has improved since, but the tendency survives. In practice it means Claude misses anything that is not the golden path. Not exotic edge cases so much as the ordinary second and third branches of a feature.

That makes Claude a poor architect. Ask it to design a codebase or shape a feature and it will produce something coherent that quietly assumes nothing goes wrong. It will not think about the failure branches unless you prompt for them specifically.

Codex over-engineers

GPT models behave the opposite way. Codex is diligent, and then keeps going. It adds hardening for conditions a three-person SaaS will never meet. The reasoning is sound every time: this edge case would break roughly once a month at a million users. You have three users. You will never hit it.

The cost is not correctness, it is size. Every piece of defensive handling has to live somewhere, so the codebase grows to carry protections against things that are not going to happen. It behaves like a senior full stack developer with decades behind them who suddenly remembers the failure they saw once in twenty years and insists on covering it.

What to use Claude Code for

Anything conversational, and anything upstream of code.

I start most features with an interview. I have Claude grill me: what the feature is supposed to accomplish, who the users are, which edge cases I have not thought about. Sometimes the goal is genuinely to shape the feature. Sometimes it is just to dump my head onto the page and let something else organise it.

From there Claude writes the plan or the RFC, does the implementation, and orchestrates sub agents. Anything involving the Agent SDK also goes to Claude, since Anthropic knows their own models best and the tooling for reading documents and working with their stack is better.

What to use Codex for

Three things: architecture review, small contained changes, and working through PR comments.

Codex is at its best when the work is bounded and the standard of correctness is high. Reviewing a plan before anyone writes code is the highest leverage use, because that is the moment Claude's blind spots are cheapest to fix. Reviewing code and reviewing PRs both work well too, though the token cost adds up quickly.

The full workflow: plan, review, implement, verify

The loop, start to finish:

  1. Interview with Claude in plan mode. The output is a plan.
  2. Codex reviews the plan and surfaces blockers, plus anything Claude did not address.
  3. Claude and Codex go back and forth, usually once or twice, sometimes more, until Codex reports no remaining blockers.
  4. Claude implements against the agreed plan.
  5. Codex reviews the implementation against that plan.
  6. Pull requests get reviewed automatically by Claude.
  7. Codex works the PR feedback, implementing the legitimate items and resolving the ones that do not match the plan.

Step seven works better than it sounds because inline PR comments carry their own context. Codex gets the surrounding lines and the comment together, which is enough to make a surgical change without loading the whole codebase into the window.

Code review produces false positives. Sometimes it flags something as incorrect that is deliberate and correct according to the plan. Those get resolved rather than fixed.

Running it as a loop with the /codex-tool command

I have a skill, invokable as /codex-tool, that tells Claude to hand the review steps to Codex through its CLI. Claude drives, Codex reviews, and the handoffs happen without me relaying messages between two terminals. It removes most of the back and forth.

I only let it run unattended on simple to medium features. On anything complex I want to read what Codex is saying, because that is where I push back. More autonomy costs control, and on complex work the control is worth more.

Here is the command file itself, verbatim. Drop it into .claude/commands/ and Claude Code picks it up as a slash command; the canonical copy lives in this gist.

---
description: Consult a persistent Codex (GPT-5) subagent via the CLI, dispatched through Monitor so the turn runs non-blocking and notifies you on completion; captures a re-invokable thread handle for follow-ups on the SAME conversation.
argument-hint: "<what to ask/task Codex> [--resume [id]] [--repo PATH]"
disable-model-invocation: true
---

You are wielding Codex as a subagent — the Codex (via CLI) equivalent of your native Agent Tool. Codex is a GPT-5 agent running a real, persistent conversation: you spawn it once and re-invoke it by handle. Treat it as a capable peer architect engineer — consult it, delegate investigation, get a second opinion.

**Same repo context as you — do NOT over-feed it.** Codex runs with `-C <repo>` and reads the full tree: it can inspect files, run `git` (log/diff/blame/show), grep the tree, and hit the network. So do NOT paste code snippets, diffs, file dumps, or long transcripts it can fetch itself; point it at paths / symbols / commands / a branch or SHA and let it look. Quote a snippet ONLY when that exact line IS the point of discussion — never as a substitute for access it already holds. It's a peer agent working the same codebase, not a blind chatbot.

**Permissions: read-only by default; full access is user-run.** The templates below run `-s read-only` — Claude Code's auto-mode permission classifier blocks `--dangerously-bypass-approvals-and-sandbox` (see `docs/sops/codex-agent-tool.md`), and read-only fully covers consults/reviews, the dominant use. When Codex must WRITE, don't silently downgrade the task: say so and have the user run the command themselves with `--dangerously-bypass-approvals-and-sandbox` in place of `-s read-only` (unrestricted internet + any file, zero approval prompts — how Alber runs Codex).

**Reasoning: ALWAYS high.** Pin `-c model_reasoning_effort="high"` on every call; never let it inherit a lower interactive default.

## One path: dispatch through Monitor

Every job goes through the SAME mechanism.

1. Write the ask to `$SP/prompt.txt` (sidesteps all shell quoting).
2. Fire a **Monitor** whose command IS the `codex` run: events → file, and a single line on exit carrying `rc` + the thread handle.
3. Keep working. Codex *exiting* is the terminal signal — Monitor notifies you in chat (`rc=0` success, `rc≠0` failure, timeout = killed). Do NOT poll or sleep.
4. On the event: **Read** `$SP/last-answer.txt` (the clean answer). The handle is in `$SP/last-thread`.

State lives in your **session scratchpad**.

### Your Agent Tool ⇄ Codex

```yaml
# native operation        →  Codex equivalent (always fired via Monitor)
- native: spawn subagent
  codex: codex exec --json                     # handle = thread.started.thread_id
- native: SendMessage(to id, prompt)
  codex: codex exec resume <id>                # follow-up, full continuity
- native: new / independent subagent
  codex: fresh codex exec                      # new thread_id
```

## Spawn (t=0)

**Step A — Bash**, write the ask:

```bash
SP="<your session scratchpad>/codex-agent-tool"; mkdir -p "$SP"   # ephemeral (fallback: $TMPDIR)
cat > "$SP/prompt.txt" <<'EOF'
<the ask — flags stripped>
EOF
```

**Step B — Monitor** (`description: "codex: <short label>"`, `timeout_ms: 1800000`, `persistent: false`), command:

```bash
SP="<your session scratchpad>/codex-agent-tool"
codex exec --json -s read-only --skip-git-repo-check \
  -c model_reasoning_effort="high" -C "${REPO:-$PWD}" \
  -o "$SP/last-answer.txt" "$(cat "$SP/prompt.txt")" </dev/null \
  >"$SP/last-events.jsonl" 2>"$SP/last-err.log"
rc=$?
grep -o '"thread_id":"[^"]*"' "$SP/last-events.jsonl" | head -1 | sed 's/.*:"//;s/"$//' > "$SP/last-thread"
echo "codex done rc=$rc thread=$(cat "$SP/last-thread")"
```

On the completion event: `rc=0` → **Read** `$SP/last-answer.txt`. `rc≠0` → **Read** `$SP/last-err.log` and report the failure.

## Follow-up (t=2) — re-invoke the same agent

Same path, same Monitor; only `exec` → `exec resume "$TID"`.

**Step A — Bash**, write the follow-up:

```bash
SP="<your session scratchpad>/codex-agent-tool"
cat > "$SP/prompt.txt" <<'EOF'
<your follow-up>
EOF
```

**Step B — Monitor** (same params), command:

```bash
SP="<your session scratchpad>/codex-agent-tool"
TID=$(cat "$SP/last-thread")            # or a specific id you tracked
codex exec -C "${REPO:-$PWD}" resume "$TID" --json --skip-git-repo-check \
  -c model_reasoning_effort="high" \
  -o "$SP/last-answer.txt" "$(cat "$SP/prompt.txt")" </dev/null \
  >"$SP/last-events.jsonl" 2>"$SP/last-err.log"
echo "codex done rc=$? thread=$TID"
```

## Params (optional)

- `--repo PATH` → substitute `REPO=PATH` so Codex inspects that codebase (`-C`). Default: cwd.
- `-m <model>` → override Codex's model. Leave default unless the user names one explicitly.
- Job expected to exceed 30 min? Raise `timeout_ms` (Monitor max `3600000` = 60 min).

## Gotchas (do NOT trip)

- The prompt goes through `$SP/prompt.txt`, never inlined — arbitrary text breaks shell quoting.
- `</dev/null` is mandatory or `exec` hangs reading stdin.
- `resume` re-stamps the thread's cwd with wherever it runs — pass `-C` consistently for repo-bound threads, but place it on the `exec` parent **before** the `resume` subcommand (`codex exec -C <dir> resume <id> …`). `codex exec resume` rejects `-C`/`-s`/`-a` as its own flags (`error: unexpected argument '-C' found`). It inherits the session's sandbox (do NOT pass `-s/-a`) but DOES accept `--dangerously-bypass…`, `-c`, `--json`, `-o`.
- Running several parallel Codex threads? Track each id yourself; `$SP/last-thread` only holds the most recent.
- Full findings & dragons: `~/Repos/codex-agent-tool/docs/` (machine-local on Alber's setup — not in this repo; the load-bearing gotchas are inlined above and in `docs/sops/codex-agent-tool.md`).

## Report back

A thread's Desktop-UI visibility is decided by its **origin source**, which `resume` never changes (verified: after CLI resumes, a seeded thread keeps `source=vscode` in both `state_5.sqlite` and the catalog):

- **Fresh `codex exec` spawn → invisible in Desktop.** It's stamped `session_source=Exec`, which the Desktop's `thread/list` filters out (only interactive sources surface: `cli`/`vscode`/`atlas`/`chatgpt`). Confirmed in source (`rollout` `INTERACTIVE_SESSION_SOURCES` + app-server `filters.rs`) and against the derived catalog the sidebar actually reads (`~/.codex/sqlite/codex-dev.db` → `local_thread_catalog`, `source_kind`). Editing `source` in raw `state_5.sqlite` does **not** fix it. So for a spawn you started, the user has **no** Desktop view — **relay what matters**. Inspect it yourself via `codex resume <id>` (CLI).

- **Seed trick (Alber's habitual mode) → visible in Desktop.** If the user seeds the conversation *in the Desktop first* (an interactive `vscode` thread) and hands you its id, drive it with `codex exec resume <id>`: the append preserves the interactive source, so the user sees the whole exchange — your follow-ups + Codex's answers — **in that Desktop thread after a full quit + reopen of Codex.app** (cold-start rehydrates from the rollout; the live app does NOT hot-reload CLI-appended turns). When the user passes you an id (`--resume <id>`), assume they intend to read it in the Desktop — write for that audience too.

Dragons: `~/Repos/codex-agent-tool/docs/external/quirks/` (Alber's machine — not in this repo).

Which should you pick if you can only pay for one?

Codex, if you are technical. The subscription stretches further, the resets are more generous, and GPT models are cheaper to run at volume. I pay for the $100 Codex plan and the $200 Claude plan, which tells you roughly how I weigh them.

Codex is harder to talk to. That is a convenience problem rather than a capability one, and if you are a developer you are already used to being precise, so it costs you very little.

If you are not technical, use Claude Code. You may have seen the line that Codex is for engineers and Claude Code is a toy. That is wrong, but there is a real observation underneath it: vibe coding with Claude Code is far easier, and if you cannot specify precisely then a tool that infers well beats a tool that demands precision.

Where you still matter

Two jobs stay with you.

The first is catching what Claude skipped. Confirm it did everything you asked. You can do this manually or hand it to another reviewer, but for anything visual, look at it yourself. Features get built correctly and still look wrong, and no reviewer working from a diff will tell you that.

The second is pushing back on Codex. When it proposes fixes that double the size of the codebase and add nothing you will ever use, those fixes are correct and unnecessary at the same time. Developers are not naturally good at rejecting correct code. Unless something is actually going to break, the complexity is not worth carrying, and that is a judgment call you have to make rather than delegate.

Does this actually ship production code?

Yes. Plenty of people still say it cannot, and they are wrong, but it does require knowing how to run it.

The honest caveat: this is software, and most software problems have been solved before. I am not shipping rockets. If you are working on something genuinely novel, at the edge of what anyone has built, the calculus may be different. For everything else it is hard to make this fail.

Related

Want this kind of thinking applied to your business?

Book a Free Value Layer Audit