なぜ README が重要か

README はリポジトリをクローンした瞬間に開発者が最初に目にするドキュメントです。 「このプロジェクトは何をするのか」「どうやって動かすのか」「自分はどこから手をつければよいか」——この3問に素早く答えられる README は、オンボーディングコストを大幅に下げます。

逆に、README がない・古い・情報が散在している状態では、新メンバーが環境構築だけで半日を溶かすことになります。経験上、「README を整備するコスト」と「新メンバーが迷うコスト × 人数」を比較すると、後者が圧倒的に大きいケースがほとんどです。

💡 README の位置づけ

README はプロジェクトの「入口ドキュメント」です。詳細はリンク先の設計書・Wiki に委譲し、README 自体はなるべく短く保つことが腐りにくさにつながります。

① プロジェクト概要(What / Why)

冒頭の数行で「このリポジトリが何をするものか」を説明します。定義すべき項目は次のとおりです。

項目内容記述例
プロジェクト名リポジトリ名と一致させる## my-app
一行説明何をするツール・サービスか社内注文管理システムの REST API サーバー
背景・目的なぜ作ったのか(Why)既存の CSV 手作業を自動化するため
主要機能箇条書き3〜5件注文登録・更新・キャンセル・CSV エクスポート
関連リポジトリフロントエンド・インフラなどmy-app-frontend / my-app-infra

⚠️ 「何をしないか」も書く

スコープを明確にするため、非対応機能や対象外ユーザーを1〜2行で書いておくと誤解が減ります。例:「決済処理は別サービス(payment-api)が担当します」

② 動作要件・前提条件

「動かない」問い合わせの多くは環境差異に起因します。以下の項目を明示してください。

カテゴリ定義すべき内容
ランタイム言語バージョン(例: Python 3.11 以上、Node 20 LTS)
OS動作確認済み OS(Windows 11 / Ubuntu 22.04 / macOS 14)
外部ツールDocker 24 以上・git 2.40 以上 など
外部サービスDB(PostgreSQL 15)・Redis 7・S3 互換ストレージなど
ネットワーク特定 IP レンジへのアクセスが必要な場合

③ インストール手順

コピー&ペーストで実行できるコマンド列を記載します。省略や「など」は禁物です。 番号付きリストで順序を明示し、各ステップの「何をしているか」を1行コメントで補足します。

Markdown — インストール手順の書き方
## インストール

### 1. リポジトリをクローン
```bash
git clone https://github.com/example/my-app.git
cd my-app
```

### 2. 依存パッケージのインストール
```bash
pip install -r requirements.txt
```

### 3. 環境変数の設定
```bash
cp .env.example .env
# .env を編集してください(次のセクションを参照)
```

### 4. データベースのマイグレーション
```bash
python manage.py migrate
```

### 5. 起動
```bash
python manage.py runserver
# → http://localhost:8000 でアクセス可能
```

手順は「新入メンバーが 0 から実行することを想定」して書く

「pip はインストール済みの前提」のような暗黙の前提は必ず明示するか、公式インストールガイドへのリンクを貼ってください。

④ 使い方(Quick Start)

最も頻繁に使われる操作を3〜5パターン示します。全機能の網羅は不要——それは別のドキュメント(API仕様書・ユーザーガイド)の役割です。

パターン記述すべき内容
基本的な使い方最もシンプルな実行例(コマンド+出力)
よく使うオプション--verbose / --config など主要フラグの説明
スクリーンショット / GIFGUI ツールや CLI の視覚的な操作感
サンプルデータ動作確認用のサンプルファイル・シードの場所

⑤ 設定・環境変数一覧

.env.example ファイルとセットで管理します。README には変数名・型・デフォルト値・用途を表形式で掲載します。 実際の値(シークレット)を README に絶対に書かないことが鉄則です。

Markdown — 環境変数テーブルの書き方
## 環境変数

| 変数名 | 型 | デフォルト | 説明 |
|---|---|---|---|
| `DATABASE_URL` | string | — | PostgreSQL 接続文字列(必須) |
| `SECRET_KEY` | string | — | Django シークレットキー(必須) |
| `DEBUG` | bool | `false` | デバッグモード。本番では `false` にすること |
| `ALLOWED_HOSTS` | string | `localhost` | カンマ区切りで許可ホストを指定 |
| `CACHE_TTL` | int | `300` | Redis キャッシュ TTL(秒) |

⑥ ディレクトリ構成

プロジェクトルートの主要ディレクトリと役割を記載します。深いネストは省略し、初見の人が「どこに何があるか」を把握できるレベルに留めます。

Markdown — ディレクトリ構成の書き方
## ディレクトリ構成

```
my-app/
├── src/          # アプリケーション本体
│   ├── api/      # REST API エンドポイント
│   ├── models/   # データモデル
│   └── utils/    # 共通ユーティリティ
├── tests/        # テストコード
├── docs/         # 設計書・ADR
├── scripts/      # 運用・開発補助スクリプト
├── .env.example  # 環境変数のテンプレート
└── requirements.txt
```

⑦ コントリビュート・ライセンス

社内プロジェクトでも「コントリビュートガイド」と「ライセンス」を明記することで、変更の進め方(ブランチ戦略・PR ルール・レビュアー)が共有されます。

項目記述すべき内容
ブランチ戦略main / develop / feature/* などの命名規則と用途
PR ルールテスト必須・レビュアー最低1名・コミットメッセージ形式など
コーディング規約linter・formatter の設定ファイルへのリンク
Issue テンプレートバグ報告・機能要望のテンプレートへのリンク
ライセンスMIT / Apache-2.0 / 社内プロプライエタリなど
連絡先担当チームの Slack チャンネルやメールアドレス

Python で実機情報を自動収集する

README に記載するバージョン情報・ディレクトリ構成は手書きすると必ず陳腐化します。 以下のスクリプトで実機から情報を自動収集し、README のドラフトを生成できます。

① 依存パッケージ一覧を自動取得

Python — requirements.txt からバージョン一覧を取得
"""
requirements.txt を読み込んでバージョン表を生成する。
README の「動作要件」セクションに貼り付ける用途。
"""
import subprocess
import sys

def get_installed_versions(req_file: str = "requirements.txt") -> list[dict]:
    """pip show で各パッケージの実際のバージョンを取得する。"""
    with open(req_file, encoding="utf-8") as f:
        packages = [
            line.strip().split("==")[0].split(">=")[0].split("~=")[0]
            for line in f
            if line.strip() and not line.startswith("#")
        ]

    results = []
    for pkg in packages:
        try:
            out = subprocess.check_output(
                [sys.executable, "-m", "pip", "show", pkg],
                stderr=subprocess.DEVNULL,
                text=True
            )
            info = {}
            for line in out.splitlines():
                if ": " in line:
                    key, val = line.split(": ", 1)
                    info[key.strip()] = val.strip()
            results.append({
                "name":    info.get("Name", pkg),
                "version": info.get("Version", "不明"),
                "summary": info.get("Summary", ""),
            })
        except subprocess.CalledProcessError:
            results.append({"name": pkg, "version": "未インストール", "summary": ""})
    return results

def print_markdown_table(packages: list[dict]) -> None:
    print("| パッケージ | バージョン | 説明 |")
    print("|---|---|---|")
    for p in packages:
        print(f"| `{p['name']}` | {p['version']} | {p['summary']} |")

if __name__ == "__main__":
    pkgs = get_installed_versions()
    print_markdown_table(pkgs)

② ディレクトリ構成を自動生成

Python — ディレクトリツリーを Markdown 形式で出力
"""
プロジェクトのディレクトリ構成を README 用に出力する。
深さ・除外パターンを設定できる。
"""
import os
from pathlib import Path

EXCLUDE = {".git", "__pycache__", ".venv", "venv", "node_modules", ".pytest_cache"}
MAX_DEPTH = 3

def print_tree(path: Path, prefix: str = "", depth: int = 0) -> None:
    if depth > MAX_DEPTH:
        return
    entries = sorted(
        [e for e in path.iterdir() if e.name not in EXCLUDE],
        key=lambda e: (e.is_file(), e.name)
    )
    for i, entry in enumerate(entries):
        connector = "└── " if i == len(entries) - 1 else "├── "
        print(prefix + connector + entry.name + ("/" if entry.is_dir() else ""))
        if entry.is_dir():
            extension = "    " if i == len(entries) - 1 else "│   "
            print_tree(entry, prefix + extension, depth + 1)

if __name__ == "__main__":
    root = Path(".")
    print(f"```\n{root.resolve().name}/")
    print_tree(root)
    print("```")

③ git 情報から最終更新日・コントリビュータを取得

Python — git log から README 用メタ情報を収集
"""
git log からプロジェクトのメタ情報を収集する。
README の「最終更新日」「主要コントリビュータ」欄の自動更新に使う。
"""
import subprocess
from collections import Counter
from datetime import datetime

def run_git(*args) -> str:
    result = subprocess.run(
        ["git", *args], capture_output=True, text=True, check=True
    )
    return result.stdout.strip()

def get_last_commit_date() -> str:
    """最新コミット日時(ISO 形式)を返す。"""
    iso = run_git("log", "-1", "--format=%cI")
    dt = datetime.fromisoformat(iso)
    return dt.strftime("%Y年%m月%d日")

def get_top_contributors(n: int = 5) -> list[tuple[str, int]]:
    """コミット数上位 n 名を返す。"""
    log = run_git("log", "--format=%aN")
    counter = Counter(log.splitlines())
    return counter.most_common(n)

def get_current_branch() -> str:
    return run_git("rev-parse", "--abbrev-ref", "HEAD")

def get_tag_list() -> list[str]:
    """最新5タグを返す(バージョン履歴に使う)。"""
    tags = run_git("tag", "--sort=-creatordate")
    return tags.splitlines()[:5]

if __name__ == "__main__":
    print(f"最終更新: {get_last_commit_date()}")
    print(f"現在のブランチ: {get_current_branch()}")
    print("\n## コントリビュータ(コミット数上位)")
    for name, count in get_top_contributors():
        print(f"- {name}: {count} commits")
    print("\n## 最新タグ")
    for tag in get_tag_list():
        print(f"- {tag}")

README のアンチパターン

アンチパターン何が問題か対策
手順が省略されている 「適宜設定してください」で詰まる人が続出する コピペで動くレベルまで具体化する
バージョンが固定されていない 「Python インストール済みの前提」で動かない マイナーバージョンまで明記(Python 3.11.x)
README が設計書の代わりになっている 長すぎて誰も読まなくなる 詳細は docs/ や Wiki にリンクする
更新されない 手順を試したらエラーになる PR マージ時に「README 更新チェック」を必須にする
シークレットが含まれている 公開リポジトリに漏洩するリスク .env.example に記載し、値は .env(gitignore)で管理

まとめ:README チェックリスト

README に最低限含めるべき7項目

□ プロジェクト概要(What / Why)
□ 動作要件(言語バージョン・外部ツール・OS)
□ インストール手順(コピペで動く番号付きコマンド列)
□ 使い方 Quick Start(最もシンプルな実行例)
□ 環境変数一覧(.env.example とセット、シークレットは書かない)
□ ディレクトリ構成(主要フォルダの役割)
□ コントリビュート方法とライセンス

これらの項目を揃えるだけで、新メンバーのオンボーディング時間は大幅に短縮されます。 Python スクリプトを CI に組み込んで README の自動チェック・自動更新を仕組み化すると、陳腐化リスクをさらに下げられます。