#0145（2025/05/22）Code First vs Schema First 

# Code First vs Schema First 

## 1. はじめに

ソフトウェア設計では「**Code First**」と「**Schema First**」という2つの開発スタイルが広く使われています。どちらを選ぶかで、チームのコミュニケーション方法や保守コスト、開発スピードは大きく変わります。本稿では GraphQL API と データベース ORM を題材に、両者の概念・利点・欠点を3000文字程度で整理し、サンプルコードを交えて実践的に解説します。

## 2. Code First とは

Code First は「**まずコードを書く**」アプローチです。開発者はクラスや型定義など実装側のコードを起点にし、ツールを使ってスキーマやマイグレーションを自動生成します。

* 実装担当者が主導権を握りやすい
* IDE 補完が効くため生産性が高い
* コードと型が 1 箇所に集約され、重複が少ない

ただし、生成物であるスキーマがブラックボックス化しやすく、非エンジニアが参照しづらい点が課題です。

## 3. Schema First とは

Schema First は「**まずスキーマを書く**」アプローチです。API 仕様書や ER 図、SDL (Schema Definition Language) をチームで合意し、その定義に沿って実装を進めます。

* 仕様が明文化されるのでチーム間で共有しやすい
* 設計レビューや契約駆動開発 (Contract‑Driven Development) に向く
* フロントエンドとバックエンドを並行開発しやすい

一方で、実装コードとスキーマを二重管理するため同期ズレが起きやすく、手間も増えがちです。

## 4. メリット・デメリット比較

| 観点         | Code First  | Schema First    |
| ---------- | ----------- | --------------- |
| 開発速度       | IDE 補完により高速 | 合意形成後は並行開発が高速   |
| ドキュメンテーション | 自動生成だとやや不透明 | スキーマがそのまま仕様書    |
| 型の重複       | 少ない         | 多くなりがち          |
| 変更フロー      | 実装主導        | 仕様主導            |
| 学習コスト      | 実装者は低い      | デザイナ・QA も参画しやすい |

## 5. ケーススタディ

### 5‑1. GraphQL API

#### Code First (TypeScript + type‑graphql)

```ts
import { ObjectType, Field, Int } from "type-graphql";

@ObjectType()
export class User {
  @Field()
  name!: string;

  @Field(type => Int)
  age!: number;
}
```

上記クラスから、下記のような GraphQL SDL が自動生成されます。

```graphql
type User {
  name: String!
  age: Int!
}
```

#### Schema First (SDL + Apollo Server)

```graphql
# schema.graphql
type Query {
  user(id: ID!): User!
}

type User {
  id: ID!
  name: String!
  age: Int!
}
```

```ts
// resolver.ts
import { Resolvers } from "./generated/graphql";
export const resolvers: Resolvers = {
  Query: {
    user: (_, { id }) => fetchUserFromDB(id),
  },
};
```

SDL を中心にリゾルバを実装する流れになります。

### 5‑2. データベース ORM

#### Code First (Python + SQLAlchemy)

```python
from sqlalchemy import Column, Integer, String
from sqlalchemy.orm import declarative_base

Base = declarative_base()

class User(Base):
    __tablename__ = "users"
    id = Column(Integer, primary_key=True)
    name = Column(String, nullable=False)
    age = Column(Integer)
```

Alembic などを通してマイグレーションファイルが生成されます。

#### Schema First (SQL + Alembic)

```sql
-- 001_create_users.sql
CREATE TABLE users (
  id SERIAL PRIMARY KEY,
  name VARCHAR(255) NOT NULL,
  age INT
);
```

```python
# models.py (SQLAlchemy のみ同期定義)
class User(Base):
    __tablename__ = "users"
    id = Column(Integer, primary_key=True)
    name = Column(String, nullable=False)
    age = Column(Integer)
```

DDL → コードの順に書くため、既存 DB のリバースエンジニアリング案件にも適します。

## 6. どちらを選ぶべきか？

| チーム状況                    | 推奨アプローチ                                  |
| ------------------------ | ---------------------------------------- |
| MVP/Fast‑Prototyping、小規模 | Code First                               |
| ドメインが複雑、コンシューマが多い        | Schema First                             |
| コンサル・SIで外部契約がある          | Schema First                             |
| モノリス→マイクロサービス移行期         | Code First で局所的に試し、最終的に Schema First へ移行 |

### 判断チェックリスト

* **仕様を非エンジニアと共有したい?** → Schema First
* **とにかく早く動く物がほしい?** → Code First
* **既存 DB を再利用するか?** → Schema First
* **型安全を保ったまま変更頻度が高い?** → Code First

## 7. まとめ

Code First と Schema First は対立概念ではなく、プロジェクトのフェーズやチーム構成に応じて使い分けるべきツールです。初期は Code First で爆速開発し、API が安定したら Schema First に切り替えて公開契約を強化する、といったハイブリッド戦略も有効です。要件の変化に合わせ、最適なアプローチを選択しましょう。