Open Reach Tech Inc.

本番が壊れていて、使える時間は 2 時間

Jiro Yamamotoのプロフィール写真
Jiro YamamotoBackend Developer

「Hora Kit の設計」シリーズの 10 本目。本番で何かが壊れていて使える時間が 2 時間しかないとき、18 の関所を通る通常経路は遅すぎます。/hora が呼ばない唯一の skill である /hora-hotfix が、不具合を 1 つ直して出し、飛ばしたものを全部書き残す 6 つの門について書きます。

Banner of 本番が壊れていて、使える時間は 2 時間

1. はじめに

この記事は、弊社(Open Reach Tech)が OSS にした AI 開発フレームワーク『Hora Kit』の設計を解説するシリーズの 10 本目です。メイン記事はこちらです。

本当の意味での自動開発を実現するためのAI開発フレームワーク『Hora Kit』をOSSにしました

用語の確認。 Hora Kit で打つコマンドは /hora の 1 つだけです。/hora が状況に応じて /hora-spec(仕様書を書く)、/hora-setup(実装リポジトリを作る)、/hora-plan(版を確定し、機能一覧と契約を書く)、/hora-build(1 機能を 18 の関所に通す)、/hora-accept(検収する)を順に呼びます。/hora-hotfix だけは /hora が呼ばず、人が直接打ちます。docs はこのうち /hora-spec を「決める側」、残りを「作る側」と呼んでいて、この記事でもその呼び方を使います。

Hora Kit の通常経路は、仕様を書き、計画し、18 の関所を通し、検収します。正しい経路なのですが、本番で何かが壊れていて使える時間が 2 時間しかないとき、この経路は遅すぎます。

/hora-hotfix はそのためのもう 1 本の経路です。不具合を 1 つ直し、出し、飛ばしたものを全部書き残します。飛ばした分は静かに消えるのではなく、後から実際の作業として戻ってきます。

/hora          機能 1 つ    →  18 の関所  →  検収済み
/hora-hotfix   不具合 1 つ  →  6 つの門   →  着地。ただし負債つき

/hora はこれを起動しません。緊急かどうかは人が決めることなので、/hora-hotfix は自分で打ちます。深夜の障害対応を経験したことがある方は、自分の会社の hotfix 手順と比べながら読んでみてください。

2. 何を諦め、何を諦めないか

諦めるもの受入レビュー、ブラウザでの実走、UX 監査、シナリオ一覧、バージョン自身の受入基準、/hora-spec、/hora-plan
残すもの落ちるテスト 1 本、全リポジトリのユニットテスト全量、触ったファイルの lint、git 規約、記録

この線引きの理由を docs はこう説明しています。時間を食っているのはユニットテストではなく、数分で終わり、しかもこの修正が他をどこか壊していないと言える唯一の根拠なので残す。何時間もかかるのはレビューのほうで、後回しにするのはそちらだ、と。

「急いでいるからテストを飛ばす」は、急いでいるときにこそ一番危ない判断だと弊社も考えていて、この線引きを hotfix 経路の出発点に置いています。

3. 6 つの門

/hora-hotfix の 6 つの門

各門には通過条件が 1 つだけあって、前の門が終わるまで次は始まりません。順に見ていきます。

H1 は受理判定です。ここで 2 つのことをします。1 つ目は、そもそも hotfix に乗るかどうかの判定で、次の 6 つに当たると乗りません。後方互換でないスキーマ変更、開いている release/<version> が未適用のマイグレーションを持つテーブルに触るスキーマ変更、他のリポジトリが作っている最中の契約の変更、依存の追加・削除・更新、新しい operation や画面やバックグラウンドジョブ、ブランチ 1 本に収まらない作業、の 6 つです。2 つ目は「直った」とは何かを決めることで、これは人が 1 文で書きます。製品がどうあるべきかを述べる文であって、それは人のものだからです。この 1 文が、この実行の受入基準のすべてになります。

H2 は再現です。落ちるテストを書きます。しかもこの不具合が原因で落ちること。修正の後ではなく、前に書きます。テストで捕まえられない不具合もあって、遅いクエリや本番データでしか出ないものがそうです。その場合は記録に reproduced: no と理由を書き、代わりに修正前と修正後の測定値を載せます。

H3 は修正です。H2 のテストを通す最小の変更だけを入れます。リファクタリングも、ついでの整理もしません。

H4 は影響範囲です。全リポジトリのユニットテストを全量走らせ、そのうえで、触った面が強制するものを実行します。

H5 は記録です。.hora/hotfix/<hotfix-id>.md を書きます。これが、この実行が飛ばしたすべての支払いなので、省略可能な項目はありません。

H6 は着地です。hotfix/<hotfix-id> を main から切り、main へ戻します。そこから枝は決して切りません。その後 /hora が、開いている release/<version> を新しい main に追従させます。

git モデル:main、release/<version>、そこから切られるブランチ

4. 断らずに、選択肢を出す

H1 で「乗らない」と判定されたとき、/hora-hotfix は断りません。docs には「夜中の 3 時に『できません』と言われても誰も助かりません」とあります。

代わりに、あなたの不具合に対する実際の代替案を組み立ててから、3 つの選択肢を出します。

1. コードだけの修正を今出す(たいていはこれ)
     露出を止める、悪い書き込みを止める、防御を足す。
     破壊的な半分は次のバージョンへ

2. patch を上げた release/<version> でやる
     通常経路。遅いが、ちゃんと検収される

3. hora の外で人がやる          (破壊的なスキーマ変更のときだけ)
     キットは門を通したふりをしません。
     誰が決め、何をしたかだけを記録に残します

/hora-hotfix が代わりに選ぶことはしません。3 番目の選択肢を意図して用意しているのには理由があって、通り抜ける道の無い門は人が迂回する門であり、そうなると何も記録に残らないからです。

5. スキーマ変更の判定は「DB を戻さずにコードだけ戻せるか」

hotfix でスキーマを触ってよいかどうか、問うべきは「マイグレーションが要るか」ではありません。「DB を戻さずにコードだけ戻せるか」です。

3 つすべてを満たすなら、マイグレーションを先に出してコードを後から出せるので、原子的なデプロイが要らなくなります。後方互換であること(いま本番で動いているコードが、マイグレーション適用後もそのまま動く)、無損失であること(既存のものを落とさない・改名しない・狭めない・上書きしない)、時間が明記されていること(実行時間とロックの挙動を、実際の行数に対する数字で述べてある)の 3 つです。3 つ目は形式的な手続きではなく、書き込みを 1 時間止める CREATE INDEX は、2 時間で出す修正としては失敗です。

起きていること一緒に出せるか
カラムが小さすぎて書き込みが落ちている出せる。広げるだけ
インデックスが無く DB が溶けている出せる。オンライン DDL。ロック時間を測って記録
一意制約が無く重複行が増え続けている半分。コード側の防御を今。制約は既存の重複を掃除してから
カラム名を間違えた半分。新カラムを足して両方に書くのを今。古い方を落とすのは後
本番データが壊れている出せない。そもそもマイグレーションではありません

壊れた行の修復はマイグレーションではありません。マイグレーションファイルは全環境で永久に再生されます。修復は、人が実行し、バックアップから戻せる一回限りのスクリプトでやります。

6. 記録に何が書かれるか

# Hotfix — session-expiry-blank

<!-- landed: 2026-08-23 -->
<!-- touches: attendance, sign-in -->
<!-- reproduced: yes -->
<!-- suites: full -->
<!-- data-damage: none -->
<!-- deferred: acceptance review, live run, UX audit, scenario list -->
<!-- debt: open -->

## What "fixed" means
<H1 で書いた 1 文>

## What backs this
- <テスト> — 修正前に落ち、修正後に通る
- ユニット全量: backend 214 passed, frontend-employee 51 passed

## What was skipped
このコードは検収されていない。…

判定語は landed で、passed ではありません。passed は /hora-accept のもので、hotfix の記録が検収と取り違えられないようにしています。touches: には変更したファイルが属する機能が入り、これが後でどの検収をやり直すかを決める行になります。

7. 負債は普通の作業として戻ってくる

hotfix のために、新しく何かを止める仕組みは足していません。負債は、既存の門が既に扱い方を知っている作業に変換されます。

次の /hora        未返済の負債を、名前とリンク付きで全部報告する

次の /hora-plan   touches: に並んだ機能それぞれについて、関所 18 を
                  [ ] に戻す。計画側のエントリも一緒に。そして debt: closed を書く

あとは通常経路です。/hora-build がその機能を拾い、/hora-accept が本来の範囲で受け入れ、それが通るまでバージョンは完了できません。

つまり hotfix はタダではなく、使うと次のバージョンの作業が増えます。これが、hotfix が全員の常用経路になることを防いでいます。

8. まとめ

緊急経路の設計で、弊社が大事だと考えているのは 3 つです。飛ばすのはレビューであってテストではないこと。断らず、選択肢を出すこと(道の無い門は迂回され、記録が残らないので)。飛ばした分を負債として記録し、通常経路に戻すこと(タダにすると常用されるので)。

深夜の hotfix を「後で全部ちゃんとやる」で済ませてしまいがちなチームの参考になれば幸いです。

原典はこちらです。 https://github.com/openreachtech/hora-core/blob/main/docs/hotfix.ja.md

送信完了!

お問い合わせいただきありがとうございます。 担当者より24〜72時間以内にご連絡いたします。