設計標準ドキュメントとは何か

設計標準ドキュメントとは、ソフトウェア開発においてチームが共通して従うべきルール・指針・判断基準をまとめた文書群です。コーディング規約、アーキテクチャ設計の方針、API設計のガイドライン、UI/UXの基準など、開発のあらゆる側面において「なぜそうするのか」「どうすればよいか」を明文化したものです。

単なる規則集ではなく、チームの集合知を記録し、意思決定の背景を伝える役割を担います。エンジニアが「この書き方で合っているのか」「この設計方針はチームの方向性と合致しているか」を判断する際の拠り所になります。

設計標準ドキュメントが対象とする範囲

コーディング規約 / アーキテクチャ設計方針 / API設計ガイドライン / UI・UXデザインガイドライン / データモデル設計標準 / テスト戦略 / セキュリティ基準 — これらすべてを包括する「チームの技術的な意思決定の集積」がここでいう設計標準ドキュメントです。

なぜドキュメント化が重要か

「口頭で共有しているから大丈夫」という状態がどれほど脆いか、多くのチームが痛い経験を通じて学んでいます。ドキュメント化の必要性を理解するために、まず「ドキュメントがない状態で何が起きるか」を具体的に見てみましょう。

ドキュメントがないと何が起きるか

実例:あるWebサービス開発チームの話

5人のチームでAPIを開発していた。チームの中で最もAPIに詳しいAさんが独自のエラーコード体系を頭の中に持っており、レビューのたびに口頭で指摘していた。Aさんが退職した翌月、新メンバー2人がそれぞれ異なるエラー形式を実装してしまい、フロントエンドとのインテグレーションで大量の修正が発生した。

このような問題は珍しくありません。「知識」が特定の人の頭の中にある限り、その人がチームを離れた瞬間に失われます。ドキュメント化は「知識を人から切り離し、チームの資産にする」プロセスです。

もう一つよくあるシナリオは「新メンバーのオンボーディングに毎回1ヶ月かかる」問題です。先輩エンジニアが毎回同じ説明を繰り返し、コードレビューで同じ指摘をし続ける。これは設計標準がドキュメント化されていれば大幅に改善できる非効率です。

ドキュメント化がもたらす4つの恩恵

恩恵 内容 具体的な効果
属人化の防止 暗黙知を形式知に変換する 特定の人の退職・異動後も知識が継続する
オンボーディング効率化 新メンバーが設計思想を素早く理解できる 「なぜこのコードはこう書かれているのか」を調べる時間が劇的に減る
レビュー基準の統一 「なんとなく気になる」ではなく、標準に基づいた建設的フィードバック レビュアーによって指摘内容が変わる問題を解消
意思決定の高速化 都度議論しなくても、既存の標準を参照することで判断できる 「前回どうしたっけ」という会話がなくなる

特に「レビュー基準の統一」は、チームの心理的安全性にも直結します。ルールが明文化されていないと、レビューが「個人の好み」に見えてしまい、「あのレビュアーのときは厳しい」という不公平感が生まれます。標準があれば、レビューはルールの確認であり、個人攻撃ではないと全員が理解できます。

コーディング規約とは

コーディング規約は最も基本的な設計標準です。コードの見た目・構造・スタイルを統一することで、誰が書いても読みやすいコードベースを維持します。

コーディング規約が整備されていないと、コードレビューで「ここのインデントが違う」「この関数名はわかりにくい」という指摘が毎回繰り返されます。レビュアーの好みや気分によってフィードバックが変わるため、レビューが不公平に感じられたり、議論に時間をとられたりします。

コーディング規約の主なカバー範囲は以下の通りです。

項目内容例
命名規則 変数・関数・クラス・ファイル名の命名パターン。getUserDatafetchUsergetUser かという議論に決着をつける
フォーマット インデント幅(2スペース vs 4スペース)、行の最大長、括弧の位置
コメントの書き方 いつコメントを書くか、どのレベルの説明を書くか。「コードで表現できることはコメントに書かない」というルールも有効
ファイル構成 importの順序(標準ライブラリ→外部ライブラリ→内部モジュール)、定数の配置場所
型の扱い TypeScriptの any 禁止、unknown の使い方、型エイリアス vs インターフェースの使い分け

実例:TypeScriptコーディング規約

TypeScriptプロジェクトでよくある規約違反と、その改善例を見ていきましょう。以下のコードは実際のレビューコメントをベースにしたものです。

TypeScript — 命名規則
// ❌ Bad: 略語・省略形の変数名
const u = await db.findUser(id);
const res = await api.get('/data');

// ✅ Good: 意図が明確な変数名
const user = await db.findUser(id);
const articleListResponse = await api.get('/articles');

// ❌ Bad: boolean変数にis/has/canプレフィックスなし
let loading = true;
let error = false;

// ✅ Good: boolean変数の命名
let isLoading = true;
let hasError = false;

// ❌ Bad: マジックナンバー(何を意味するか不明)
if (user.age >= 18) { /* ... */ }
setTimeout(fn, 86400000);

// ✅ Good: 定数として意味を持たせる
const ADULT_AGE = 18;
const ONE_DAY_MS = 24 * 60 * 60 * 1000;
if (user.age >= ADULT_AGE) { /* ... */ }
setTimeout(fn, ONE_DAY_MS);

命名規則に加えて、エラーハンドリングの規約も重要です。「エラーを握りつぶすな」という指摘はよくありますが、「どう書けばよいか」まで示すことでドキュメントの価値が高まります。

TypeScript — エラーハンドリング規約
// ❌ Bad: エラーを握りつぶしている
async function fetchUser(id: number) {
  try {
    const user = await api.getUser(id);
    return user;
  } catch (e) {
    console.log(e);  // ログだけ出してundefinedを返す
  }
}

// ❌ Bad: エラー型が不明確(any型は使わない)
async function fetchUser(id: number) {
  try {
    return await api.getUser(id);
  } catch (e: any) {
    throw e;
  }
}

// ✅ Good: 型安全かつ呼び出し元で処理を委ねる
class UserNotFoundError extends Error {
  constructor(userId: number) {
    super(`User not found: ${userId}`);
    this.name = 'UserNotFoundError';
  }
}

async function fetchUser(id: number): Promise<User> {
  const user = await api.getUser(id);
  if (!user) {
    throw new UserNotFoundError(id);
  }
  return user;
}

Good例は「なぜ良いか」まで書く

上記のGood例では UserNotFoundError という具体的なエラークラスを使うことで、呼び出し元が instanceof で判別できます。また Promise<User> という戻り値の型から「必ずUserが返る、失敗したらthrowする」という契約が読み取れます。この説明をドキュメントに加えることで、「なぜこう書くのか」が伝わります。

Linter / Formatter との連携

コーディング規約はドキュメントとして書くだけでなく、ESLint・Prettier・RuboCop・gofmt などのLinter/Formatterと連携させることで、自動的に強制できます。「書いてあるが守られない」状態を防ぐために、ツールによる自動チェックをCIに組み込むことが理想です。

たとえばESLintの設定で「any 型の使用を禁止する」ルールを設けると、CIがPRをブロックするため、ドキュメントを読んでいなくても自然と準拠したコードが書かれるようになります。これが「ドキュメントの自動執行」の考え方です。

JSON — .eslintrc.json 設定例
{
  "rules": {
    "@typescript-eslint/no-explicit-any": "error",
    "@typescript-eslint/no-unused-vars": "error",
    "prefer-const": "error",
    "no-console": ["warn", { "allow": ["error", "warn"] }],
    "eqeqeq": ["error", "always"],
    "no-var": "error"
  }
}

Prettierはフォーマットの自動修正に使います。「タブかスペースか」「セミコロンありかなしか」といった議論をPrettierに委ねることで、チームの議論コストをゼロにできます。

「ドキュメントで書いたルールは、可能な限りツールに落とし込む」という原則を守ると、人間が守るべきルールは「判断を要するもの(設計の意図・例外の判断など)」だけに絞られ、レビューの質が向上します。

既存ガイドをベースにする戦略

コーディング規約をゼロから作ると「タブかスペースか」のような不毛な議論が発散しやすいため、まず業界標準を採用し、例外事項のみを明記する方針を取ると良いでしょう。

言語ベースにする既存ガイドチームでの追加事項の例
Python PEP 8 行の最大長を88文字に変更(Black互換)、型ヒント必須
JavaScript / TypeScript Airbnb Style Guide any型の使用禁止、カスタムエラークラスの命名規則
Go Effective Go エラーラッピングの規約(fmt.Errorf("context: %w", err)
Java Google Java Style Guide ロギングライブラリの指定(SLF4J + Logback)

「既存のガイドを参照しつつ、チーム固有のルールを追加する」形を取ることで、外部の知見を活かしながらチームの実情に合わせた規約を短時間で整備できます。

次のPARTで扱うこと

PART 02では、コーディング規約より抽象度の高い「アーキテクチャ設計標準」と「API設計ガイドライン」を解説します。Clean Architectureのレイヤー構成、ADRの書き方、APIレスポンス形式の統一方法など、チーム全体の設計方針を文書化する方法を学びます。