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)
import { ObjectType, Field, Int } from "type-graphql";
@ObjectType()
export class User {
@Field()
name!: string;
@Field(type => Int)
age!: number;
}
上記クラスから、下記のような GraphQL SDL が自動生成されます。
type User {
name: String!
age: Int!
}
Schema First (SDL + Apollo Server)
# schema.graphql
type Query {
user(id: ID!): User!
}
type User {
id: ID!
name: String!
age: Int!
}
// resolver.ts
import { Resolvers } from "./generated/graphql";
export const resolvers: Resolvers = {
Query: {
user: (_, { id }) => fetchUserFromDB(id),
},
};
SDL を中心にリゾルバを実装する流れになります。
5‑2. データベース ORM
Code First (Python + SQLAlchemy)
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)
-- 001_create_users.sql
CREATE TABLE users (
id SERIAL PRIMARY KEY,
name VARCHAR(255) NOT NULL,
age INT
);
# 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 に切り替えて公開契約を強化する、といったハイブリッド戦略も有効です。要件の変化に合わせ、最適なアプローチを選択しましょう。