「コードは読まない」開発スタイル ― Mermaid図解とモジュール分割で人間とAIの分業を作る

前回、Claude Codeと1日でリアルタイム音声AIを作った。今回はその続き。開発を続けるうちに、ある割り切りに行き着いた——人間はコードを読まない。読むのはAIで、人間は図と意図を持つ。この分業を成立させるために2本のPRを重ねた記録である。

図をdraw.ioではなくMermaidで書く理由

「仕組みをUMLで描いてほしい、コードは読まないから」と頼んだところ、返ってきた提案はdraw.ioではなくMermaid(テキストで書く作図記法)だった。理由は3つ。

  1. GitHubがREADMEやMarkdown内で自動レンダリングする
  2. テキストなのでPRで差分レビューできる
  3. AIが生成・更新できる=実装を変えるPRに図の更新を同梱できる

図はコードと同じで、現実とズレた瞬間に価値がマイナスになる。draw.ioのXMLは差分が読めず、更新が億劫になり、確実に腐る。Mermaidならコードと同じレビューの土俵に乗る。

こうして docs/architecture.md に6枚の図が入った。全体像、中継サーバーの2ループ構造(「両耳に受話器を持つ通訳者」)、Push-to-Talkの1ターン、function callingの伝票フロー、認証の門番ページ、切断と再接続の状態遷移。「実装を変えるPRでは該当する図も更新する」という運用ルールもREADMEに明記した。

実際、この後のPRで図に赤が入った。「Responses APIもOpenAIなんだから、OpenAIと分かるように囲んだほうがいい」——コードを読まない人間が図を見て設計の曖昧さを指摘し、AIが図とコードを直す。分業が回り始めた瞬間だった。

522行のmain.pyを7つに割る

図が揃ったところで、522行に育ったmain.pyを責務単位に分割した。

backend/
├── main.py      # FastAPIの組み立てとルーティングだけ(90行)
├── config.py    # 環境変数
├── auth.py      # Cognito検証
├── relay.py     # WebSocket中継の本体
├── personas.py / history.py / search.py

面白いのは、この分割を最初からやらなかったこと。当初「分割の必要ある?」と聞いたら、AIの答えは「今はない。トリガー(2個目のツール追加・800行超え・共同開発者の出現)を決めて、それまで触らない」だった。リファクタリングは「読みにくくて困った実績」が出てからやるのが一番安い。今回は次の機能(WebRTC実装)が控えていたので、その前準備として発動した。

挙動は1ミリも変えない純リファクタリングだが、検証は全経路E2E(認証・WebSocket・実際の音声往復)で行った。純リファクタリングこそ回帰が怖い。

余談: 直コミットにキレる人の話

docsだけの修正をPRなしでmasterに直コミットする運用にしたとき、昔の記憶が蘇った。以前、似たことをして30行のSlackで詰められたことがある。

いま思えばあれはプロセスの欠陥だった。masterへの直コミットがそんなに重大なら、ブランチ保護(GitHubの設定1つ)で物理的に不可能にできる。それをせず人間の自制心に任せ、破られたら感情で詰めるのは「機械が防ぐべきことを人間を責めて解決しようとする」行為だ。ポカヨケの思想——通れる扉を開けておいて、通った人を怒鳴るのは、扉の設計者の敗北である。

というわけで、我が家の運用はこうなった: 個人リポジトリは「挙動が変わればPR、docsは直コミット」のハイブリッド。共有リポジトリを持つ日が来たら、初日にブランチ保護を入れる。ルールは感情ではなく設定で守る。

コードは yoshiharu-ishii/realtime_voice で公開している。次回はWebRTC実装編。


リアルタイム音声AI 開発シリーズ

シリーズ目次(全7回のあらすじ)はこちら

コメントする

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

上部へスクロール