1. 動いた。しかし翌日には壊れた
自動化を作ったことがある人なら、この経験に覚えがあるはずだ。昨日まで動いていたものが、今日突然動かなくなる。エラーメッセージを読む。原因を探す。修正する。また動く。そして数週間後、また壊れる。
これは自動化の品質の問題ではない。構造の問題だ。
外部サービスのUIをスクレイピングして自動投稿していた時期がある。しばらくは動いた。しかしサービスがデザインを変えるたびに、スクリプトが壊れた。修正のたびに時間が消えた。「自動化のメンテナンス」という矛盾した作業が発生し続けた。
問題の本質は一つだった。制御権が自分の手になかった。
2. 境界という概念
ソフトウェア設計に「境界」という概念がある。システムの中で自分がコントロールできる領域と、そうでない領域を分ける線だ。
自分の境界の内側にあるもの——自分のコード、自分のデータ、自分のリポジトリ——は、自分の意思で変えられる。壊れたら直せる。仕様を変えたければ変えられる。
外側にあるもの——外部サービスのAPI、プラットフォームのUI、他社の認証システム——は、相手の都合で変わる。自分には止める手段がない。
自動化が長く動き続けるかどうかは、この境界の引き方で決まる。外側への依存が多いほど、壊れる頻度が上がる。
3. Qiita自動公開で起きたこと
技術記事の自動公開システムを構築しようとした日のことだ。GitHubにMarkdownをpushすると、自動でQiitaに記事が公開される仕組みを作ろうとした。
最初のエラーは `QiitaNotFoundError` だった。既存の記事IDが見つからないというエラーだ。原因を調べると、別のアカウントで管理されていた記事のIDがファイルに残っていた。IDを削除して再実行した。
次のエラーは `--all` オプションの誤解だった。全記事を公開するオプションのはずが、`ignorePublish: true` の設定を無視して全件処理しようとした。個別指定に切り替えた。
次は `'public/path/to/file.md' is not found`。Qiita CLIはパスではなくbasenameで指定する仕様だった。ドキュメントをよく読んでいなかった。
記事が公開されたと思ったら、同じ記事が十数本重複投稿されていた。pushのたびにActionsが走り、毎回新規投稿していたからだ。IDが書き戻されていなかった。
IDを書き戻そうとしたら `403 Forbidden`。ActionsのGITHUB_TOKENにwrite権限がなかった。
一つ直すたびに次の問題が出た。それぞれは小さなエラーだが、積み重なると半日が消える。
4. 何が問題だったのか
振り返ると、問題のパターンは明確だった。
第一に、外部サービスの仕様を正確に把握していなかった。`--all` の挙動、basenameの指定方法、IDの扱い——これらはQiitaとQiita CLIの仕様であり、自分の境界の外にある。外側の仕様を誤解したまま自動化を組むと、予期しない動作が起きる。
第二に、権限の設定が抜けていた。GitHub ActionsのGITHUB_TOKENはデフォルトでread-onlyだ。書き込みには明示的な設定が必要だが、それを見落としていた。権限もまた、自分が明示的に設定しなければならない境界の一部だ。
第三に、「べき等性」が設計されていなかった。同じ操作を何度実行しても同じ結果になる性質をべき等性という。記事の公開処理がべき等でなかったため、重複投稿が発生した。IDの有無でチェックするロジックを入れることで初めて解決した。
5. 解決した設計
最終的に動いた設計はシンプルだった。
記事ファイルに `id: null` が設定されている場合のみ新規投稿する
投稿後にQiitaが発行したIDをファイルに書き戻してcommitする
次回以降はIDがあるのでスキップされる
べき等性が確保された。何度pushしても、未公開の記事だけが公開される。IDが書き戻された記事は二度と重複しない。
この設計が動くようになって初めて、「自動化」と呼べるものになった。それまでは「壊れ続けるものを直し続けること」だった。
6. 自動化の三つの条件
この経験から、長く動き続ける自動化には三つの条件があると考えるようになった。
一つ目は「外部依存を最小化する」。外部サービスのAPIを使うのは避けられないが、依存する箇所を絞り込む。UIではなくAPIを使う。APIは変わりにくいが、UIは頻繁に変わる。
二つ目は「べき等性を設計する」。同じ操作を何度実行しても安全な設計にする。状態を確認してから操作する。「すでに存在するか」「すでに処理済みか」を必ずチェックする。
三つ目は「境界を明示する」。自分がコントロールできるものとできないものを明確に分ける。コントロールできないものへの依存は、失敗したときのフォールバックを用意する。
7. 壊れることは情報だ
自動化が壊れたとき、苛立ちを感じるのは自然なことだ。しかし壊れ方には情報がある。
どのエラーが出たか。どこで止まったか。どの前提が崩れたか。それらを読むと、自分の設計がどこで外部依存していたかが見える。壊れることは、境界の引き方が甘かった場所を教えてくれる。
今回の連続エラーは苦労だったが、結果として設計が堅牢になった。べき等性のチェック、IDの書き戻し、権限の明示的な設定——これらは最初から入れるべきだったが、壊れることで気づいた。
自動化の失敗は、次の自動化を強くする材料だ。
8. 長く動かすということ
1000年動き続けるシステムを考えるとき、自動化の堅牢性は避けて通れないテーマだ。外部サービスは消える。APIは変わる。プラットフォームは終了する。
だから境界を設計する。自分のデータは自分のリポジトリに置く。外部への依存は最小限にする。壊れたときに何が残るかを考えて作る。
自動化とは、人間が介在しなくても動き続ける仕組みだ。それが本当に動き続けるためには、壊れにくい境界と、壊れても回復できる設計が必要だ。
「pushしたら公開される」という一行の自動化の裏に、その設計がある。
自動化が壊れる理由は、たいてい境界の外に制御権がある。外側の依存を減らし、べき等性を設計し、境界を明示する。それが長く動き続ける自動化の条件だ。