


非同期Rustのサービスを、キャンセルや失敗に強く作る
非同期Rustでサービスを作る時に使う。モックで済ませず本物のコードを最後まで書き、エラーとキャンセルを安全に扱い、利用者が使う本物のサービスの入口からテストし、全部のチェックを通してから完了を伝える。
このAIルールを使うには
## モックやスタブで済ませず、本物の実装を最後まで作る
要求された動作を最初から最後まで完成させる。本番用ロジックの不足を、モック、代替実装、スタブ、テスト専用の近道で置き換えてはならない。
- 実際の依存関係と実際の統合経路を使う。
- 壊れた実装を隠すためにテストを操作しない。
- 必須の準備を迂回する環境上の回避策を使わない。
- どうしても残る未完了作業は、最終的な計画または仕様に明記する。
- 利用者が実際に通る経路から得た根拠なしに、機能が動くと決して主張しない。
自律的な実行とは、完了の定義を縮小することではなく、厳密な経路を最後までやり遂げることである。
## 関係するデータの種類すべてに、同じ設計の判断を当てはめる
システムが関連する複数の信号群またはデータ群を扱う場合、現在は1つの群を優先していても、すべての群に共通する動作を設計する。
- 取り込み、保存、検索、分析、ライフサイクル管理に共通する意味を特定する。
- 各群に固有の最適化は明示的な境界の内側に保つ。
- 共通の判断が、最初に実装した群を意図せず優遇しないことをテストする。
- すべての経路で、エラー、設定、可観測性に関する一貫した動作を保つ。
- 実装のずれを放置せず、意図した違いを文書化する。
優先順位は提供順を決めてもよいが、アーキテクチャの範囲を暗黙に狭めてはならない。
## 安全を1番、速さを2番、手軽さを3番にする
安全性を第1、性能を第2、開発者の利便性を第3とする。最適化は、不変条件と失敗時の動作を理解した後に限って行う。
- 公開境界で無効な入力を拒否する。
- 明示的な上限を名前付き定数として定義し、超過時には構造化されたエラーを返す。
- 依存関係、ストレージ、ネットワークの障害を、成功扱いの代替処理で覆い隠さない。
- 予期しない状態は、成功した何もしない処理ではなく、エラーまたは不変条件違反として扱う。
- 欠落した任意データは、契約で任意と明示されている場合に限って許容する。
性能改善では、失敗、再試行、キャンセル、途中まで進んだ状態でも正しさを保たなければならない。
## 中の前提はデバッグ用アサーションで、悪い入力はエラーで扱う
レビュー担当者が内部の不変条件を検討できるよう、デバッグ時だけ有効なアサーションで不変条件を記述する。最適化されたビルドではデバッグ用アサーションが消える可能性があるため、無効な利用者入力、外部データ、実行時条件には復旧可能なエラー結果を使う。
- ライブラリエラーは`Result`と`?`演算子で伝播させる。
- 再利用可能なライブラリコードでは、無条件に値を取り出すメソッドを避ける。
- すべての失敗を平坦化せず、意味のあるエラー型を保つ。
- エラーに情報が含まれる場合は、失敗し得るファイルシステム操作とメタデータ操作を使う。
- 元の原因を失わずに文脈を加える。
- 未使用の代入によってエラーを暗黙に無視してはならない。
起こり得ない状態は、診断に十分な形で明確に失敗させる。
## タスクがキャンセルされても壊れない非同期コードを書く
非同期処理は、どの中断点でもキャンセルされ得ると考える。キャンセルによって更新途中の状態が外部から見えたり、データが失われたりしないように状態遷移を構成する。
- キャンセルの影響を受ける可変状態には、アクター形式の所有権とメッセージ受け渡しを優先する。
- await地点をまたいでロックを保持しない。
- 変更を不可分に行う必要がある場合は、同期的なクリティカルセクションを使う。
- タスクを無理に中断せず、キャンセル信号を通じて協調的に停止する。
- 選択ループでは、処理中のFutureを作り直して破棄せず、pin留めして再開する。
- 終了処理と後片付けを冪等にする。
アクターコードや非同期コードでは、所有権とキャンセル時の動作が安全になるなら、単純な複製や共有所有権を選ぶ。
## 型と所有権を分かりやすくし、大きいファイルを分ける
新しい概念を生の文字列で渡さず、確立された領域識別子と型別名を使う。混在させてはならない値を型システムで区別する。
- 状態遷移が分かりにくくなる場合は、関数内で名前をシャドーイングしない。
- 所有権が局所に残り、複製が不要な場合は参照を返す。
- 値がタスク境界を越える場合は、共有所有権を明示する。
- イテレーターの連鎖によって失敗し得る制御フローが読みにくくなる場合は、手続き的なループを優先する。
- 大きすぎるファイルと関数は、まとまりのある責務ごとに分ける。
- 公開インターフェースを実装の詳細から分離する。
コードが非同期状態を調整する場面では、読みやすさが信頼性を支える。
## 利用者が通る本物のサービスの入口からテストする
実際の公開スタックを通じて機能を動かすまで、完成と判断してはならない。利用者がネットワークプロトコル、ゲートウェイ、サービス境界を通じて動作へ到達する場合、内部関数を直接呼び出さず、その境界を通してテストする。
1. 実際のサービスを代表的な設定で起動する。
2. サポートされている取り込みインターフェースまたはコマンドインターフェースから入力を送る。
3. サポートされている問い合わせインターフェースまたは応答インターフェースから結果を取得する。
4. 永続化、エラーの対応付け、観測可能な副作用を確認する。
5. 内部の動作と公開された動作が一致することを確認する。
内部テストは有用だが、本番経路を通じた証明の代わりにはならない。
## 目的ごとにテストの種類を選び、失敗のテストも入れる
分離されたロジックには高速な単体テストを、広い状態空間にはプロパティベーステストを、耐障害性には障害注入テストを、サービス全体の動作には統合テストを使う。
- 有用な反例が得られる範囲まで、プロパティテストの入力を絞る。
- ストレージまたはプロトコルの動作が重要な場合は、実際のサービスフィクスチャを使う。
- 非決定的なスケジューリングを意図的に許容するテスト群に限って再試行を使う。
- 変更が復旧または状態遷移に影響する場合は、障害点の網羅率を確認する。
- 新しい公開エンドポイントごとに統合テストを追加する。
- 成功経路と同じ慎重さでエラー経路をテストする。
再試行を繰り返して、決定論的な不具合を見かけ上の合格へ変えてはならない。
## チェックは、固定されたツールチェーンとリポジトリのコマンドで動かす
ビルド、書式設定、lint、テスト、文書チェック、ライセンスチェック、依存関係監査、スペルチェックは、リポジトリで定義されたコマンドから実行する。
- 固定されたツールチェーンと指定されたフォーマッターチャンネルを使う。
- 統合テスト群の前に宣言済みのローカルサービスを起動し、終了後に停止する。
- 実際の認証情報を埋め込まず、文書化された環境変数を通じてテスト設定を渡す。
- まず影響を受ける狭い範囲のパッケージを実行し、次に全機能を有効にしたワークスペースのテスト群を実行する。
- lintの警告はすべて、解消する作業または対象を最小限に絞った許容理由が必要なものとして扱う。
- 各変更が、後で積み重ねる変更の一部としてだけでなく、単独でもコンパイルできることを確認する。
標準ワークフローが行う準備を保つ場合に限り、ツールを直接呼び出してよい。
## アーキテクチャの変化を、3種類の記録に分けて残す
アーキテクチャの変更は、相互に補完する次の3種類の記録で追跡する。
- **特性:** アーキテクチャが満たすべき先回りの要件。
- **ギャップ:** インシデント、テスト、本番時の動作から判明した制約。
- **逸脱:** 推奨アーキテクチャから意図的に外れた点と、その理由および制約。
システムの保証を変えるコード変更は、該当する記録へ関連付ける。目標とする設計を完成済みの動作と誤認しないよう、形式モデル、シミュレーションのワークフロー、意思決定記録、実装状況を区別して保つ。
新機能の発表時だけでなく、根拠によってアーキテクチャが変わった時にも記録を更新する。
## レビューコメントへの返信は、そのコメントのスレッドの中でする
差分の焦点を絞り、説明を対応する判断に結び付けて、変更をレビューしやすくする。
- 同じ場所に別のコメントを作らず、既存のスレッド内でレビューコメントへ返信する。
- 返信が意図した親コメントに結び付いていることを、ホスティング画面で確認する。
- 一見して理由が分からない不変条件、許容事項、複製、逸脱が必要な理由を説明する。
- 無関係なリファクタリングを機能変更から分離する。
- 繰り返す失敗パターンから永続的な規則が判明したら、随時更新される指針を改訂する。
レビューを整然と保つことで文脈が維持され、同じ問題が複数の会話で並行して解決される事態を防げる。
## 完了を伝える前に、最後の差分で全部のチェックを通す
完了を報告する前に最終差分を調べ、次を確認する。
- 適用されるすべてのlintが、説明のない警告を残さず合格している。
- 変更したすべてのソースファイルとテストファイルで、書式チェックが合格している。
- 無条件の値取り出し、暗黙のエラー抑制、不当な複製が残っていない。
- 本番用インターフェースのテストと、影響を受ける統合テストが合格している。
- 必須ヘッダー、ログ規則、文書、依存関係監査が合格している。
- 新しいファイルと関数の焦点が絞られ、保守可能な状態にある。
- 認証情報や機密値が含まれていない。
失敗は原因から修正する。合格結果を得るために、チェックを無効にしたり、テストを弱めたり、本番経路を迂回したりしてはならない。
