# Claude Opus 4.6 vs Codex GPT-5.4: сравнение AI-кодеров на Go-задачах


Последние месяцы я использую два AI-инструмента для повседневной разработки:
[Claude Code][claude-code] (Anthropic, текущая модель Opus 4.6) и
[Codex CLI][codex-cli] (OpenAI, текущая модель GPT-5.4). Оба работают в терминале,
оба умеют читать файлы, запускать тесты и писать код. Но ведут себя по-разному.

На реальной задаче в рабочем проекте я заметил, что Codex на medium effort
быстрее находит root cause бага и предлагает более короткий фикс, чем Claude на
high effort с контекстом в миллион токенов. Это показалось неожиданным — и я
решил проверить наблюдение на контролируемых задачах.

Сразу оговорюсь: **два бенчмарка — не статистика**. Выводы ниже — наблюдения,
а не доказательства. Для полноценного сравнения нужно больше задач, разные типы
(диагностика, рефакторинг, работа с большой кодовой базой) и повторные запуски.
Тем не менее, даже два эксперимента показали интересные паттерны.

## Методология

Обе модели получают одинаковый промпт с описанием задачи. Работают в изолированных
директориях, параллельно. Замеряю wall time, количество tool calls, размер
финального кода и прохождение тестов.

| Параметр | Claude | Codex |
|---|---|---|
| Модель | claude-opus-4-6 (1M context) | gpt-5.4 |
| Effort | high (subagent) | medium |
| Режим | Claude Code Agent (feature-implementer) | codex exec (full-auto) |

Задачи взяты из [Exercism Go track][exercism-go] — готовые тесты, чёткая
спецификация, самодостаточность.

## Задача 1: Tournament (средняя сложность)

Реализовать функцию `Tally(io.Reader, io.Writer) error` — парсинг результатов
футбольного турнира, подсчёт статистики, вывод форматированной таблицы.
Обработка комментариев, пустых строк, невалидного ввода.

### Результаты

| Метрика | Claude | Codex |
|---|---|---|
| Время | ~38 сек | ~60 сек |
| Тесты | 17/17 | 17/17 |
| Размер кода | 111 строк | 113 строк |
| С первой попытки | Да | Да |

Оба решения используют один и тот же подход: `bufio.Scanner` +
`map[string]*struct` + `sort.Slice`. Различия косметические:

- Codex выделил helper-функции (`recordWin`, `recordDraw`, `standingsFor`),
  использовал `switch` вместо `if/else if`, обернул ошибки через `%w`
- Claude добавил явную валидацию спецсимволов в именах команд

**Вердикт: ничья.** Задача слишком проста, чтобы выявить разницу — оба решают её
с первой попытки за минуту.

## Задача 2: React (высокая сложность)

Реализовать reactive system: input cells с изменяемым значением, compute cells
с зависимостями от других ячеек, автоматическое распространение изменений по
графу зависимостей, callbacks с возможностью отмены. Включая diamond dependency
problem — callback должен сработать один раз, даже если ячейка зависит от
изменённого input через несколько путей.

Это уже архитектурная задача: нужно спроектировать граф зависимостей,
topological sort для пропагации, механизм callbacks.

### Результаты

| Метрика | Claude | Codex |
|---|---|---|
| Время | ~148 сек | **~84 сек** |
| Тесты | 14/14 | 14/14 |
| Размер кода | 221 строка | **165 строк (-26%)** |
| Tool calls | 30 | **~12** |
| Токены | 68K | **23K** |

Оба решения корректны — все 14 тестов проходят с `-race`. Но архитектура
принципиально отличается.

### Как решил Claude (221 строка)

Четыре типа: `reactor`, `cell` (базовый), `inputCell` (embedding), `computeCell`
(расширенный). Отдельные `baseDependencies` и `computeDependencies`. Две функции
вычисления: `compute1` и `compute2`. Пропагация — итеративная: собрать все
зависимые ячейки, затем в цикле обновлять те, чьи зависимости уже обновлены,
пока не останется необновлённых. В худшем случае O(n^2).

Helper `getBaseCell()` с type switch извлекает базовый `*cell` из `inputCell`
или `computeCell`. Canceler использует self-referencing pointer trick — хранит
указатель на себя как ключ map.

### Как решил Codex (165 строк)

Три типа: `reactor`, `cell` (единый для input и compute), `canceler`. Input от
compute отличается наличием `compute func() int` — closure, которая захватывает
зависимости при создании. Никаких `compute1`/`compute2`, никакого `getBaseCell`.

Пропагация — DFS для сбора достижимых ячеек, затем сортировка по ID создания.
Поскольку зависимости всегда создаются раньше зависимых ячеек, сортировка по ID
гарантирует правильный порядок — элегантная замена полноценного topological sort.
O(n log n).

Callbacks откладываются: сначала полная пропагация всех значений, потом вызов
callbacks. Это гарантирует, что callback видит стабильное состояние системы.

Canceler — простой nil-check: `Cancel()` обнуляет ссылку на cell, повторный
вызов безопасен.

### Ключевые различия

| Аспект | Claude | Codex |
|---|---|---|
| Типы | 4 (cell, inputCell, computeCell, canceler) | 3 (reactor, cell, canceler) |
| Compute function | Отдельные compute1/compute2 | Единая closure `func() int` |
| Topological sort | Итеративная convergence O(n^2) | Sort by creation ID O(n log n) |
| Callbacks | Во время пропагации | После полной пропагации |
| Type assertions | `getBaseCell()` type switch | Прямой `dep.(*cell)` |

Решение Codex проще, короче и алгоритмически лучше. Трюк с сортировкой по ID
создания — красивое решение, которое Claude не нашёл, выбрав более
"академический" подход с итеративной convergence.

## Что я думаю

Два бенчмарка — недостаточно для выводов. Это exploratory эксперимент, не
научное исследование. Нужны задачи на диагностику багов, работу с большими
кодовыми базами, рефакторинг, iterative refinement. Возможно, на других типах
задач расклад будет обратным.

Тем не менее, наблюдения совпадают с моим опытом на реальных проектах:

**Codex на medium effort тратит меньше токенов на compliance и больше — на
задачу.** У моей конфигурации Claude Code загружено ~900 строк правил и
чеклистов. Это полезно для production-кода, но на бенчмарке означает, что модель
тратит ресурсы на соблюдение TDD-цикла, pattern-first алгоритма,
pre-commit чеклиста — вместо того чтобы просто писать код. Codex получает
минимальный системный промпт и тратит все токены на решение.

**Codex склонен к более простым архитектурным решениям.** Единый тип вместо
иерархии, closure вместо отдельных полей, sort by ID вместо iterative
convergence. Это может быть свойством модели, а может — следствием medium
effort: меньше "думает", меньше over-engineers.

**Claude более "defensive" и формальный.** Отдельные типы для input и compute,
explicit type safety, больше комментариев. Для большого проекта с командой это
может быть преимуществом. Для решения задачи — overhead.

После этого эксперимента я упростил свои rule-файлы для Claude Code (с 777 до
436 строк) и добавил "Fast Path" в workflow — для багфиксов и задач с понятной
реализацией модель пропускает обязательный TDD-цикл и code review loop. Посмотрим,
как это повлияет на скорость.

## Воспроизведение

Задачи из [Exercism Go track][exercism-go]:
[Tournament](https://exercism.org/tracks/go/exercises/tournament),
[React](https://exercism.org/tracks/go/exercises/react).

Запуск Claude Code:

```bash
# Через Agent (feature-implementer subagent)
# Промпт с описанием задачи + "run go test -v -race ./..."
```

Запуск Codex:

```bash
codex exec -m gpt-5.4 \
  --dangerously-bypass-approvals-and-sandbox \
  -C /path/to/exercise \
  "описание задачи"
```

[claude-code]: https://docs.anthropic.com/en/docs/claude-code
[codex-cli]: https://github.com/openai/codex
[exercism-go]: https://exercism.org/tracks/go

