Agent-First Development

リポジトリ = 知識ベース

はじめに

Agent-First Development における鉄則がある:

リポジトリにないものは、エージェントにとって存在しない。

Slack で共有された設計判断、口頭で伝えられた規約、誰かの頭の中にあるドメイン知識。これらはエージェントにとっては「存在しない」。エージェントが参照できるのはリポジトリ内のファイルだけだ。

なぜリポジトリに集約するのか

エージェントの視界

エージェントが見えるもの:
  [リポジトリ内のファイル] ← これがすべて

エージェントが見えないもの:
  - Slack の会話
  - Notion のページ
  - 誰かの頭の中の知識
  - 過去のミーティングでの合意
  - 「暗黙の了解」

人間にとっても良い

リポジトリに知識を集約することは、エージェントのためだけではない:

  • 新メンバーのオンボーディング: 「あの人に聞いて」が不要になる
  • バス係数の改善: 特定の人に依存する知識が減る
  • 意思決定の追跡可能性: なぜそう決めたかの記録が残る
  • 非同期コミュニケーション: タイムゾーンや勤務時間に依存しない

docs/ ディレクトリの構造設計

推奨構造

docs/
  architecture/        # アーキテクチャの設計判断
    overview.md        # システム全体像
    data-flow.md       # データフロー
    auth-design.md     # 認証・認可の設計

  conventions/         # コーディング規約、命名規則
    coding-standards.md
    naming-conventions.md
    error-handling.md

  design/              # 機能ごとの設計ドキュメント
    patient-management.md
    appointment-scheduling.md
    billing-integration.md

  exec-plans/          # 実装計画(エージェントへの指示書)
    patient-search-api.md
    notification-system.md

  references/          # 外部仕様、API ドキュメント
    fhir-resource-mapping.md
    insurance-api-spec.md

  guides/              # 手順書
    adding-new-feature.md
    database-migration.md
    deployment.md

  adr/                 # Architecture Decision Records
    001-use-postgresql.md
    002-clean-architecture.md
    003-event-driven-notifications.md

各ディレクトリの役割

architecture/

システムレベルの設計判断。変更頻度は低いが、重要度は高い。エージェントが「なぜこの構造なのか」を理解するための基盤。

conventions/

コーディング規約やパターン。エージェントが一貫したコードを生成するために参照する。理想的には linter ルールと対応させる。

design/

機能ごとの設計ドキュメント。ビジネスロジックの背景、ユースケース、制約を記述する。エージェントがドメイン知識を得る主要なソース。

exec-plans/

具体的な実装計画。エージェントに渡す「作業指示書」として機能する。完了後はアーカイブするか design/ に統合する。

adr/ (Architecture Decision Records)

「なぜその技術を選んだのか」「なぜこの設計にしたのか」を記録する。エージェントが設計の背景を理解し、一貫した判断を下すために重要。

AGENTS.md: テーブルオブコンテンツとしての設計

アンチパターン: 百科事典型

# 悪い例: すべてを AGENTS.md に書く

## コーディング規約
(200 行の規約...)

## アーキテクチャ
(300 行の設計説明...)

## API 仕様
(500 行の仕様...)

この問題:

  • ファイルが巨大になり、更新が困難
  • エージェントが重要な情報を見落とす
  • 情報の重複が発生し、矛盾が生まれる

推奨パターン: ポインタ型

# AGENTS.md

## Overview
raica IDP のバックエンド。患者管理、予約管理、請求管理を提供する
医療 SaaS の中核システム。

## Quick Start
- `make build` - ビルド
- `make test` - テスト実行
- `make lint` - lint 実行
- `make migrate` - DB マイグレーション

## Directory Structure
src/
  domain/    → ドメインモデル。外部パッケージへの依存禁止。
  api/       → HTTP ハンドラ。domain の interface を通じてデータアクセス。
  infra/     → DB 接続、外部 API クライアント。
  pkg/       → 共有ユーティリティ。

## Key Documents
- Architecture: docs/architecture/overview.md
- Coding Standards: docs/conventions/coding-standards.md
- Error Handling: docs/conventions/error-handling.md
- Adding Features: docs/guides/adding-new-feature.md

## Critical Rules
1. domain/ は外部パッケージに依存してはならない
2. 患者データを扱う API は必ず auth middleware を通す
3. DB クエリには必ず context を渡し、timeout を設定する

ポイント: AGENTS.md は 50-80 行程度に収める。詳細は別ファイルへのポインタで。

Progressive Disclosure パターン

3 層構造

Layer 1: AGENTS.md (常に読まれる)
  ↓ ポインタ
Layer 2: docs/ 内の主要ドキュメント (必要に応じて参照)
  ↓ ポインタ
Layer 3: ソースコード内のコメント、テストケース (深い理解が必要なとき)

実装例

Layer 1 (AGENTS.md):

## Error Handling
→ docs/conventions/error-handling.md を参照

Layer 2 (docs/conventions/error-handling.md):

# Error Handling

## 原則
- エラーは必ず wrap して上位に返す
- エラーメッセージには原因特定に必要な情報を含める
- ユーザー向けエラーとシステムエラーを分離する

## パターン
→ 実装例は src/api/handlers/medication.go L45-80 を参照

## カスタムエラー型
→ src/domain/errors/types.go を参照

Layer 3 (ソースコード):

// WrapError はエラーをドメインエラーとして wrap する。
// 使用例:
//   return WrapError(err, "failed to fetch patient", PatientNotFound)
//
// エラーメッセージには以下を含めること:
// - 何をしようとしたか
// - なぜ失敗したか(可能であれば)
// - 影響を受けるリソースの識別子
func WrapError(err error, msg string, code ErrorCode) error {

ドキュメントの鮮度維持: Doc Gardening

問題: 古いドキュメントは害になる

古いドキュメントはないのと同じどころか、エージェントに誤った情報を与えるためむしろ有害だ。

対策 1: ドキュメントに日付とオーナーを記載

---
title: 患者検索 API 設計
author: @yamada
created: 2025-11-15
last_reviewed: 2026-02-01
status: active  # active | draft | deprecated
---

対策 2: 自動 staleness チェック

# .github/workflows/doc-freshness.yml
name: Document Freshness Check
on:
  schedule:
    - cron: '0 9 1 * *'  # 毎月1日
jobs:
  check:
    runs-on: ubuntu-latest
    steps:
      - name: Check stale docs
        run: |
          # 90 日以上更新されていないドキュメントを検出
          find docs/ -name "*.md" -mtime +90 -print

対策 3: PR テンプレートにドキュメント更新チェック

## PR Checklist
- [ ] 関連するドキュメントを更新したか?
- [ ] 新しい規約やパターンを docs/ に追加したか?
- [ ] AGENTS.md の更新が必要か確認したか?

対策 4: 定期的な Doc Gardening セッション

月に一度、チームでドキュメントの棚卸しを行う:

  • deprecated なドキュメントを削除またはアーカイブ
  • 内容が古いドキュメントを更新
  • 不足しているドキュメントを特定して作成

Slack から Design Doc への移行

よくある問題

Slack #backend チャンネル:
  田中: 「患者検索のページネーションどうする?」
  佐藤: 「cursor-based がいいと思う」
  田中: 「了解、offset はパフォーマンス問題あるもんね」
  鈴木: 「limit は 50 にしよう」

  → 3 週間後、この会話は流れて誰も覚えていない
  → エージェントはこの議論に一切アクセスできない

解決策: Discussion → Decision → Document

1. Slack で議論する(これは自然なこと)
2. 結論が出たら design doc に書く
3. design doc を PR で追加する
4. Slack に design doc へのリンクを貼る

Design Doc の例:

# docs/design/patient-search-pagination.md

## 背景
患者検索 API にページネーションを追加する必要がある。

## 検討した選択肢
1. Offset-based: シンプルだが、大量データでパフォーマンス劣化
2. Cursor-based: 一貫したパフォーマンス、実装はやや複雑

## 決定
Cursor-based pagination を採用する。

## 理由
- 患者数が増加しても一貫したパフォーマンス
- リアルタイムデータ更新時にページずれが発生しない
- 既存の medication API も cursor-based で統一感がある

## 仕様
- cursor: 最後のレコードの ID(UUID)を base64 エンコード
- limit: デフォルト 20、最大 50
- レスポンスに next_cursor を含める

Try This: リポジトリ知識ベース化

Exercise 1: 知識の棚卸し

チームの知識がどこにあるか棚卸しする:

  1. Slack の #backend チャンネルの過去 1 ヶ月の技術的な議論を 5 つピックアップする
  2. それぞれについて「この情報はリポジトリ内にあるか?」を確認する
  3. ないものは design doc として追加する

Exercise 2: Progressive Disclosure の実装

自分のリポジトリで Progressive Disclosure を実装する:

  1. AGENTS.md を作成(または改善)する - 50 行以内
  2. AGENTS.md から参照される docs/ 内のドキュメントを 3 つ作成する
  3. Claude Code で「このリポジトリに新しい API を追加する方法を教えて」と聞いて、エージェントが正しいドキュメントにたどり着けるか確認する

Exercise 3: ADR の作成

過去にチームで行った技術的な意思決定を 1 つ選び、ADR として記録する:

# ADR-XXX: [タイトル]

## Status
Accepted

## Context
[なぜこの決定が必要だったか]

## Decision
[何を決めたか]

## Consequences
[この決定の結果、何が変わるか]

まとめ

リポジトリを知識ベースとして機能させるための原則:

  1. If it's not in the repo, it doesn't exist - すべての重要な情報をリポジトリ内に
  2. Progressive Disclosure - AGENTS.md は入り口、詳細は別ファイルへ
  3. Doc Gardening - ドキュメントは作って終わりではなく、継続的にメンテナンスする
  4. Discussion → Document - Slack の議論は design doc に昇華させる
  5. Pointer, not copy - 情報の重複を避け、Single Source of Truth を維持する

次のステップ