🗒️

このはてしなく遠いAPIドキュメント坂をのぼる

に公開

この記事は2025 ZAICO アドベントカレンダーの13日目の記事です。

この記事のまとめ

「自社プロダクトで利用しているAPIに関するドキュメントが全然なく、追加するにしても対応すべき数が多くて手動だと大変」というつらみが、
「Claude Codeのカスタムコマンドでドキュメント生成を一般化・自動化」して解決に向かい始めた、というお話になります。
はてしない、というのは量のことなので、あまり深い話はありません。

あらすじ

自分は最近入社したのですが、ふと見てみたらWebやiOS・Androidアプリなど内部で利用するAPIに関するAPIドキュメントの内容が実際のAPIエンドポイントの数と比べて不足していることに気づきました。
ははーん、これはきっと開発の途中からドキュメントを書く文化が生まれたんだな、なるほどなるほどとのん気に捉えていたのも束の間、調べていく内に約10年続いているプロダクトの中でたぶん直近に導入されたのか全体のざっと3割くらいしかドキュメントが書かれておらず、また最新バージョンのiOS/Androidアプリから実際にリクエストされているAPIの半分以上はドキュメントがないということがわかってきました。

モバイルエンジニアの悲しみを終わらせよう。

出てくる言語やツール達

1. 今あるAPIエンドポイントを把握する

まずは現状把握だ!

  • APIはURLの接頭辞として /api が含まれるので rails routes | grep api を実行してリスト化
    • 別途Claude Codeのplan modeで調査してもらい、不足がないか確認
  • アプリ側で実際に利用しているAPIエンドポイントを洗い出し/共有してもらい対応の優先度付け

調査の結果、約300近いAPIエンドポイントがあることが判明しました。
また例えば /users のような /api の接頭辞がないルーティングで respond_to を使い format.json でレスポンスを返すアクション達も出てきました。
こういったタイプはモバイルアプリでは使っていないと開発のガイドラインに書いてあったので今回は無視する予定だったのですが、実際は使っていました。ちょっと!
format.json でgrepしてアクションを1つずつ見てエンドポイントを確認・・・というのは面倒くさいので、このタイプの一覧出力はClaude Codeくんにお任せしつつ、あとは漏れが判明したら随時対応することにします。

2. APIドキュメントを記述していく

ドキュメントがないAPIエンドポイントの網羅と優先付けが出来たので、APIドキュメントを充実させていくぞ!

弊社プロダクトの現状のAPIとAPIドキュメントに関する運用は以下の通りです。

  • render json: ~~~ か jbuilder を使ってAPIレスポンスを返している
  • TypeSpecでAPIスキーマを定義 -> openapi.yamlを生成する
  • RSpecのRequest SpecなどでAPIのリクエストが行われると、openapi.yamlに記述されたスキーマと実際のリクエスト・レスポンスに差異がないか検証され、差異があるとテストがfailedになる (committee gemの設定)

そのため、以下を各エンドポイントに対して行っていく流れになりそうです。

  1. TypeSpecでAPIスキーマを定義
  2. openapi.yamlを生成
  3. テストを実行し、スキーマに問題がないことを確認 (テストがなければ最低限正常系のテストケースを作成する)

こちらも、定義する前にリクエスト・レスポンスを1つずつ実装から確認していくのは面倒くさいなどうしようかなと考えた結果、
「せや!RSpecのテストコードからリクエストを、実行結果からレスポンスを確認しよう!」と思い至りました。
処理の実態に対してテストケースが足りていない部分は今後補充することにして、今回は一旦ドキュメントの最低限の作成を優先していきます。

これでAPIドキュメントの作成手順と準備が整ったので、あとは書いていくだけ!

やり方に問題がないか確認がてら手作業で1件ドキュメントを追加した後、当然ながら思いました。
「優先的に対応すべきエンドポイントだけでもあと数十件あるってのに手動でやっている場合か??」

3. 自動化する

我々にはAIの加護がある!自動化するぞ!
(弊社では2025/12時点でClaude Codeの利用を軸にしており、Maxプランの5x/20xの利用が自由です)

APIドキュメントの作成手順がシンプルかつ機械的に行えるものだと踏んだので、これはClaude Codeにお願いしても大丈夫だろうとコマンドを作成しました。

api-schema-generate.md
---
argument-hint: [HTTP_METHOD] [ENDPOINT] [OPTION]
description: 指定したAPIエンドポイントに対するAPIスキーマの生成・更新を行う
allowed-tools: Write, Edit, MultiEdit, Bash({openapi.yaml生成コマンド})
---

# 実装例
!git log -p {参考となるcommitのid}

## TypeSpecの設計
- `api_schema/internal/spec/models`
  - APIのレスポンスで表現したいデータ構造ごとに、RailsのModel名単位でファイルを分割する
    - 例: ユーザー(User)に関するオブジェクト -> `api_schema/internal/spec/models/user.tsp`
  - レスポンス用のスキーマのデータ構造は**必ず** `api_schema/internal/spec/models` に配置するファイルに記述し、 `api_schema/internal/spec/routes` 側から参照される関係にする
  - ページネーションなど、RailsのModelではない共通のオブジェクトは適切なファイル名で保存
    - 例: `api_schema/internal/spec/models/pagination.tsp`
- `api_schema/internal/spec/routes`
  - APIエンドポイントをベースとし、RailsのModel名単位でファイルを分割する
    - 例: APIエンドポイント = `/api/users/:id` -> TypeSpecのroutes = `api_schema/internal/spec/routes/users.tsp`
    - 例: APIエンドポイント = `/api/users/:user_id/articles/:id` -> TypeSpecのroutes = `api_schema/internal/spec/routes/users/articles.tsp`

# 指示
1. `spec/requests` に格納されているRequest Specから $1 $2 のAPIエンドポイントに関するテストを行うテストケースを探す
  - テストケースがない場合、実装例を参考に正常にレスポンスが返るだけのテストケースを追加
2. 該当のテストケースを実行し、実際のリクエストとレスポンスを理解する
  - 複数のテストケースが存在する場合、リクエスト・レスポンスのスキーマが異なる場合があるので網羅的に情報を収集
  - テストケースに `pp` などのコードを追加して実際のリクエストとレスポンスを出力し、そのAPIエンドポイントに関する最新のAPIスキーマを把握
  - APIスキーマを把握したら追加したコードを削除
3. APIエンドポイントに対応するControllerのactionの処理を分析し、手順2で得た情報に不足がないか多角的に分析
4. 最新のAPIスキーマを把握したら、TypeSpec形式でAPIスキーマを記述
5. openapi.yamlを生成
  - どのモジュールのファイルを変更したかで実行すべきコマンドが異なる
    - 本体: `{openapi.yaml生成コマンド}`
    - xxxモジュール: `{openapi.yaml生成コマンド}`
    - yyyモジュール: `{openapi.yaml生成コマンド}`
6. openapi.yamlが正常に生成されたら、該当のAPIエンドポイントに対するRequest Specを全て実行し、正常に成功するまで以下のフィードバックループを繰り返す
  a. TypeSpecを修正
  b. openapi.yamlの再生成
  c. Request Specの再実行

# オプション
OPTION引数が設定されていて、以下のいずれかと**完全に一致する場合**はそれぞれの動作を実行する

## `--create-branch`
- 指示の手順を開始する前にgitのbranchを作成する
  - `git checkout -b api-docs-{method}-{endpoint}`
  - methodのフォーマット
    - 引数のHTTP_METHODを以下のように加工したもの
      - HTTP_METHODを小文字に置換
  - endpointのフォーマット
    - 引数のENDPOINTを以下のように加工したもの
      - `/``_` に置換
      - `:id``__id__`, `:xxx_id``__xxx_id___`, `:page``__page__` のように置換
  - branch名の例
    - 例) `GET /api/users` -> `api-docs-get-api_users`
    - 例) `DELETE /api/users/:id` -> `api-docs-delete-api_users___id__`

## `--auto-run`
- 指示の手順を完了後、変更をgitにcommit -> git push -> Pull Requestの作成 まで行う

### 手順の詳細
- `git commit` する時
  - 新規ファイルが追加されている事が多いので、事前に `git add -A` を実行
  - コミットメッセージは以下のフォーマットを採用
    - フォーマット: `{Type}: {HTTP_METHOD} {ENDPOINT} のAPIスキーマ`
      - `Type` には Add, Fix, Deleteのいずれかを入力
        - Add: APIスキーマを新しく追加する時
        - Fix: APIスキーマを修正した時
        - Delete: APIスキーマを削除した時
        - もしいずれかにも当てはまらない場合、適した単語を入力
      - `HTTP_METHOD`, `ENDPOINT` にはコマンド実行時の引数をそのまま入力
- Pull Request を作成する時
  - 本文のフォーマットは以下を採用
    ```md
    {例を記述}
    ```
  - gh CLIがインストールされておらず `gh` コマンドが実行出来ない場合、PR作成をせずに手動でPR作成をする場合のガイドをユーザーに提示して対応を完了とする

## `--auto-run-with-create-branch`
- オプションの `--create-branch` の指示を実行した後にAPIスキーマを生成し、手順が完了したらオプションの `--auto-run` の指示を実行

ものぐさなので、branchの作成からPR作成まで一気通貫でやらせるオプションも追加しています。
これで、人間が行う作業はClaude Codeを起動してコマンドを実行するだけになりました。
master(main) branch上で --auto-run をしてmasterに直pushするなどの可能性もあるため、必要に応じてGitHub等でBranch protection rules的な設定をしましょう (1敗)

実際に使ってみたところ、コマンドの引数は $1, $2 じゃなくても argument-hint の内容でも伝わりそうだったり、オプションは完全一致した時に実行してねと指示しているけど1-2文字の誤字なら気持ちを汲み取って実行してくれたり、割とよしなに対応してくれます。

ついでにSkillsにもほぼ同様の内容を追加し、普段の開発の流れでも同じ手順で作業をしてくれるようにしましたが、こちらは狙い通りにSkillを参照してくれるかまだ検証段階です。

AI側でスキーマ記述→問題があるとテストが失敗→スキーマを見直して再度テストを実行、のループを自律的に行うことができるため、AIにAPIドキュメントの更新を任せる時にCommitteeはとても有用でした。
不足したAPIドキュメントを爆速で埋めるためにAIを活用するぞ!というのが今回のきっかけだったのですが、今後の実態とドキュメントの乖離の是正もこのコマンドを使ってAIにお願いできそうです。

終. 今後の展望

コマンドをリポジトリ内に格納し、リポジトリを触ったことのないモバイルエンジニア側でも(何ならエンジニアじゃない人達でも)ドキュメントを作成できるようになったので、人海戦術で課題を解決できるようになりました。
また、現在は人間のレビューを通してからbranchをmergeするようにしているのですがCommitteeによってテストが通る = APIスキーマは問題なさそうであることはある程度担保されるので、ゆくゆくはCIが通り次第自動mergeとかにもしていきたい所存ですし、そもそもコマンドの実行→mergeまで全部自動化できると理想的です。

ようやくのぼりはじめたばかりのこのAPIドキュメント坂、今後も大AI時代に適した運用を考えていきます。


次回はoryeigerさんのOpenTelemetry話です。お楽しみに!

ZAICO Developers Blog

Discussion