📘

Claude CodeとCodex簡単に比較してみた

デジタルマーケティング事業部エンジニアの石原です。
本記事では、すでにほかの方もたくさんの記事を出しているであろうAnthropicのClaude CodeとOpenAIのCodexを用いて、簡単なTodoアプリを開発した際の比較結果を紹介します。両者はいずれも自律的にコードを生成・修正できるコーディングエージェントであり、開発効率を大きく変える可能性を持ちます。本稿では、実際に動作を検証した経験をもとに、その特徴や使い勝手をまとめます。

注意:2025年10月時点での情報になります。


今回の目的

比較の目的は以下の2点である。

  • 自律型コーディングエージェントとしての実用性を検証する
  • Claude CodeとCodexの挙動や生成結果の違いを明確にする

今回はTodoアプリの開発を題材に、作業フロー成果物に焦点を当てる。なお、各サービスへの登録方法やインストール手順については割愛する(検索すれば容易に確認できるため)。

Claude CodeとCodexの概要

項目 ClaudeCode Codex
開発元 Anthropic OpenAI
動作環境 ローカルのターミナルやIDE上 クラウドベース(Codex CLIはローカル実行可)
得意分野 複雑なコードベースの理解、大規模リファクタリング コード生成、テスト作成、汎用的な開発タスク
MCPサーバ対応 あり あり
個人利用料金 20ドル(約3,000円Claude契約) 約2,860円(ChatGPT Plus経由)

Claude Codeはより長大なコンテキストを扱える点が強みであり、大規模なプロジェクトに適している。一方Codexは実装スピードとUIの統合性に優れる。
とされている。

開発条件と設計

Todoアプリの要件

  • ブラウザで動作するWebアプリ
  • Todo一覧(タイトル表示)、詳細(本文・日付表示)
  • 新規登録・更新・削除が可能

技術スタック

  • フロントエンド:Next.js(TypeScript)+Shadcn+TailwindCSS
  • 状態管理:Zustand
  • バックエンド:Hono(TypeScript)
  • DB:PostgreSQL
  • 開発環境:Docker+docker-compose
  • ビルドツール:pnpm(corepackで対応)
  • セットアップ:makeコマンドでマイグレーション等を自動化

これをChatGPTに読み込ませて仕様書を作成、それを各AIツールにコピペして実装をしてもらう。

ChatGPTで生成したTodoアプリの仕様書を開く
# 目的
Webブラウザで動作するTodo管理アプリの実装を、LLM(自律型開発AI)がそのまま読み取りコード生成できるように、必要十分な仕様・設計・ディレクトリ構成・API契約・環境変数・ビルド/実行手順・非機能要件をテキストで定義します。図表は用いません。

---

## 前提・スコープ
- Webアプリ(SPA/SSR)として動作します。
- フロントエンド: **Next.js(TypeScript) + TailwindCSS + shadcn/ui**、状態管理は **zustand**- バックエンド: **Hono(TypeScript)**- DB: **PostgreSQL**- コンテナ: frontend / backend / db を **docker-compose** で一括起動。
- パッケージマネージャ: **pnpm(corepack enable)**。初回は lockfile がなくても動作させます。
- **make コマンド**でローカル環境構築、マイグレーション、起動を自動化します。
- 認証は範囲外(本要件に含まれない)。マルチユーザー前提の認可も範囲外。まずは単一ユーザー想定。

---

## 要求機能
- Todo一覧表示(タイトルの一覧)
- Todo詳細表示(タイトル・本文・登録日・更新日)
- Todoの新規登録、更新、削除

---

## エンティティ設計

### エンティティ名
`Todo`

### スキーマ(PostgreSQL)

| カラム名 || 制約 | デフォルト |
|-----------|----|------|-------------|
| id | UUID | PK | gen_random_uuid() |
| title | VARCHAR(100) | NOT NULL |  |
| body | TEXT | NULLABLE |  |
| created_at | TIMESTAMP WITH TIME ZONE | NOT NULL | now() |
| updated_at | TIMESTAMP WITH TIME ZONE | NOT NULL | now() |

#### 制約 / 仕様
- `title` は 1〜100 文字。
- `body` は 0〜10,000 文字程度を上限想定(アプリ側バリデーション)。
- 物理削除(ソフトデリート無し)。
- 並び順は基本 `updated_at DESC`---

## API設計(REST)

### ベースURL
- docker 内: `http://backend:4000`
- ホスト: `http://localhost:4000`
- Content-Type: `application/json`
- エラーレスポンスも JSON で返す。

### 共通エラーフォーマット
{
  "error": {
    "code": "VALIDATION_ERROR | NOT_FOUND | INTERNAL_ERROR",
    "message": "...",
    "details": { "field?: string", "reason?: string" }
  }
}

### 一覧取得

`GET /todos?limit={int}&offset={int}`
省略時: `limit=50`, `offset=0`, 最大 `limit=100`

**200**

{
  "items": [
    {"id":"uuid","title":"...","updated_at":"ISO8601"}
  ],
  "total": 123
}

---

### 詳細取得

`GET /todos/:id`

**200**
{
  "id":"uuid",
  "title":"...",
  "body":"...",
  "created_at":"ISO8601",
  "updated_at":"ISO8601"
}

**404**`NOT_FOUND`

---

### 新規作成

`POST /todos`

**Request**
{ "title": "...", "body": "..." }

**バリデーション**

* title: 1〜100 文字、必須
* body: 0〜10000 文字、任意

**201**
{
  "id":"uuid",
  "title":"...",
  "body":"...",
  "created_at":"ISO8601",
  "updated_at":"ISO8601"
}

**400**`VALIDATION_ERROR`

---

### 更新

`PUT /todos/:id`
全置換(id はパスで指定)

**Request**
{ "title": "...", "body": "..." }

**200** → 詳細取得と同形式
**404**`NOT_FOUND`

---

### 削除

`DELETE /todos/:id`

**204**(ボディ無し)
**404**`NOT_FOUND`

---

### ヘルスチェック

`GET /healthz`**200**
{ "ok": true }

---

## バリデーション / スキーマ共有

* **zod** を用いてスキーマを定義し、`packages/shared` に配置。

| スキーマ名              | 内容                                      |
| ------------------ | --------------------------------------- |
| TodoCreateSchema   | title, body                             |
| TodoUpdateSchema   | title, body                             |
| TodoDetailSchema   | id, title, body, created_at, updated_at |
| TodoListItemSchema | id, title, updated_at                   |

フロント/バック両方で同一スキーマを使用し、型の重複定義を防止。

---

## ディレクトリ構成(pnpm ワークスペース / モノレポ)

ルート直下に `pnpm-workspace.yaml` を配置。
管理パッケージ:

* `apps/frontend`
* `apps/backend`
* `packages/shared`

/ (repo root)
  .env
  .env.local.example
  docker-compose.yml
  Makefile
  pnpm-workspace.yaml
  package.json

  /apps
    /frontend
      next.config.ts
      package.json
      src/
        app/
          layout.tsx
          page.tsx
          todos/
            page.tsx
            [id]/page.tsx
            new/page.tsx
            [id]/edit/page.tsx
        components/
        lib/
          api.ts
          store.ts
          validators.ts
        styles/globals.css
      tailwind.config.ts
      postcss.config.js
      tsconfig.json

    /backend
      package.json
      tsconfig.json
      src/
        index.ts
        routes/
          todos.ts
          health.ts
        lib/
          db.ts
          repo.ts
          validators.ts
      prisma/
        schema.prisma
        migrations/

  /packages
    /shared
      package.json
      tsconfig.json
      src/
        schemas.ts
        types.ts

---

## 技術スタック詳細

* **Next.js**: App Router、サーバーアクション未使用(API経由統一)
* **UI**: shadcn/ui
* **CSS**: TailwindCSS
* **状態管理**: zustand
* **取得**: fetch API(`lib/api.ts`* **バックエンド**: Hono + @hono/zod-validator
* **ORM**: Prisma
* **DB**: PostgreSQL
* **ログ**: console(将来 pino 等導入余地)
* **CORS**: フロントのオリジンのみ許可

---

## 環境変数

### `.env`(開発環境のサンプル例なので、本番環境のそれとは異なる)

POSTGRES_USER=app
POSTGRES_PASSWORD=app
POSTGRES_DB=tododb
POSTGRES_PORT=5432
DATABASE_URL=postgres://app:app@db:5432/tododb
BACKEND_PORT=4000
FRONTEND_PORT=3000
FRONTEND_ORIGIN=http://localhost:3000

* backend: `DATABASE_URL`, `BACKEND_PORT`, `FRONTEND_ORIGIN`
* frontend: `NEXT_PUBLIC_BACKEND_URL`(例: `http://localhost:4000`---

## Docker / Compose 設計

* **db**: `postgres:16`(ボリューム永続化)
* **backend**: `node:22-alpine`

  * corepack enable
  * `pnpm install --no-frozen-lockfile`
  * `pnpm prisma generate`
  * 起動時 `pnpm prisma migrate deploy`
  * `apk add --no-cache openssl`
* **frontend**: `node:22-alpine`

  * `pnpm dev -p 3000`
* 共通ネットワーク: `appnet`
* backend → db 接続名: `db`

---

## Make タスク

| コマンド         | 内容                                       |
| --------------- | ------------------------------------------ |
| make setup      | corepack enable, pnpm install              |
| make lock       | lockfile 生成                               |
| make up         | docker-compose up(db → backend → frontend)|
| make down       | docker-compose down                        |
| make logs       | ログフォロー                                |
| make db-migrate | migrate deploy                             |
| make db-reset   | 開発専用リセット                             |
| make dev        | ホットリロード起動                           |

---

## バックエンド実装詳細(Hono)

* **CORS**: origin: FRONTEND_ORIGIN
* **JSON パース制限**: 1MB
* `/healthz` ルート
* `/todos` ルート:

  * 入力バリデーション: @hono/zod-validator + shared
  * repo.ts で CRUD 実装
  * db.ts で Prisma 初期化
* **エラーハンドリング**

  * ZodError → 400
  * NotFound → 404
  * その他 → 500

---

## フロントエンド実装詳細(Next.js)

* **ルート**

  * `/todos` : 一覧
  * `/todos/[id]` : 詳細
  * `/todos/new` : 作成
  * `/todos/[id]/edit` : 編集
* **zustand**

  * 一覧のページング状態、キャッシュ
  * フォーム一時保持
* **lib/api.ts**

  * getTodos, getTodo, createTodo, updateTodo, deleteTodo
* **UI**

  * shadcn/ui(Button, Input, Textarea, Card, Table)
  * エラーメッセージ表示

---

## マイグレーション(Prisma)

model Todo {
  id         String   @id @default(uuid())
  title      String   @db.VarChar(100)
  body       String?  @db.Text
  createdAt  DateTime @default(now())
  updatedAt  DateTime @updatedAt
  @@map("todos")
}

### コマンド例

pnpm prisma generate
pnpm prisma migrate dev --name init
pnpm prisma migrate deploy
pnpm prisma migrate reset -f

---

## ローカル実行フロー

1. `make setup`
2. `make lock`(初回のみ)
3. `.env` 作成
4. `make up`
5. ブラウザで [http://localhost:3000/todos](http://localhost:3000/todos)

---

## セキュリティ / 非機能要件

* CORS 明示設定
* Renovate 等による依存更新
* 入力上限: 1MB
* ログに機密情報を含めない

コーディング中の人間の関与

自律型エージェントとはいえ、完全自動ではない。人間の介入ポイントは以下の通りである。

  • コマンド実行に対する許可確認(セキュリティ対策)
  • 工程間の進行判断(チェックポイント管理)
  • エラー発生時の対応(自分で修正または、AIにログを渡して修正提案を求める)

比較結果

項目 Codex Claude Code
バージョン codex-cli 0.39.0 1.0.120
モデル gpt-5-codex Claude 3.5 Sonnet
初回アプリ生成時間 約15分 約10分
最終完成までの時間 約55分 約60分
トークン使用量 約316K 約7,233K
備考 軽量で効率的 Readme自動生成など気配りあり

Claude Codeは23倍トークン消費が多い(ログ取得ライブラリによる差異の可能性あり)が、細部までドキュメント化してくれる点が特徴的である。

生成物比較

UI

UI面では両者ともにほぼ互角であり、Next.js+Shadcn構成を正確に再現した。

仕様準拠具合・機能

  • Claude Codeの生成物もCodexの生成物もTodoの新規作成、編集、削除、詳細表示といった一連のCRUD操作ができることを確認。
  • いずれもDockerで動作し、パッケージマネージャーや使用するライブラリも指定した通りの実装を実現している。

結局どちらを使えばいいのか?

本検証の範囲では大きな差は見られなかった。どちらも十分に実用レベルに達しており、用途や開発規模に応じて選択すれば良い。(2025年10月現在)。

  • すでにChatGPT Plusを利用しているなら、Codexを使うのが手軽である。
  • Claude Codeは巨大プロジェクトに強く、コンテキスト長の面で有利。
  • ただし、両者ともに個人利用時はトークン上限があるため注意が必要(Claudeは一定時間で回復、Codexはも一定時間上限と週上限あり)。
  • MCPサーバ(Serena MCP)を利用すればCodexでも長文タスクを効率的に処理できることが期待される。

MCP(Model Context Protocol)とは?

MCPはAnthropicが2024年11月に発表した大規模言語モデル(LLM)などのAIが、AIが外部サービスを操作できる共通ルールである。
Claude CodeやCodexではこれを用いることで、AIが外部ツールやサービスと連携可能になる。

機能 内容
外部データの参照 ファイルシステムやDB、Web情報の取得(context7等)
外部サービス連携 Slack、GitHub、Figmaなどとの統合
アクション実行 メール送信、スケジュール登録、リファクタリングなど
コードのコンテキスト理解 大規模なコードを効率よく理解する(Serenaを用いる)

今後のAI活用では、このMCP対応が鍵になると考えられる。

まとめ

  • Claude CodeとCodexはカスタマイズなしでもほぼ互角の性能を示した。
  • 実際の差は「裏で動くモデル」と「コンテキスト処理能力」に依存すると考えられる。
  • MCPを活用すれば、より柔軟な自律開発環境を構築できる。

人間に求められる役割は、要件定義・設計・成果物検証の3点である。
単にコードを生成するのではなく、「プロダクト意識」を持ってAIを活用していくことが、今後の開発者に求められる姿勢である。

オープン開発チーム

Discussion