


大きな多言語のランタイムのコードを、今までの動きを壊さずに直す
ネイティブとスクリプトの両方で書かれ、パッチを当てた依存関係を中に持つ大きなランタイムのプロジェクトで使う。近くのガイドを先に読み、プロジェクトのビルド用ショートカットとテストターゲットを使い、独自の型と禁止事項を守り、動きの変更は互換性フラグの後ろに入れる。
このAIルールを使うには
## 触るコードに一番近いガイドと、親プロジェクトの有無を先に確かめる
この種のリポジトリにおけるガイドは階層構造になっている。ルートのガイドは高レベルの全体像を示し、サブディレクトリ内のガイドは主要クラス、参照先の対応表、ローカルの慣習、アンチパターンといったコンポーネント固有の文脈を担う。ルートのガイドに加えて、これから触れるコードに最も近いガイドを必ず読むこと。
- リポジトリが単独でチェックアウトされたのか、より大きな親プロジェクトのコンポーネントとしてネストされているのかを必ず判別する。ネストされている場合、重要なドキュメントは親のルートに存在し、ここからは見えない。何かを前提にする前に、親のreadme、親のガイド、その他のトップレベル文書を読むこと。
- 自由に調査し読んでよいが、編集を提案または実施する前にこの文脈を集めておくこと。
- ガイドに欠けている、長期的に有用な高レベル情報を見つけた場合は、会話の中だけに留めず、追加を提案すること。
## プロジェクトのショートカットで、ビルドとテストをする
本プロジェクトには、正式なビルドシステムが1つと、その上に薄く重ねられたショートカット群としてのタスクランナー層がある。日常の作業ではショートカットを使い、ショートカットが必要なオプションを公開していない場合に限って、基盤となるビルドツールに降りること。場当たり的なコンパイルコマンドを勝手に作らないこと。
探して再利用すべき典型的なショートカットは次のとおり。ビルド、全テストの実行、フォーマット、スクリプト系ソースのlint、ネイティブ系ソースのlint、システム言語パッケージのlint、デバッグ用のテスト出力ストリーミング、単一の互換性スイートまたは適合性スイートの実行、新規テストの雛形生成、型定義の生成、エディタ用インデックスデータベースの生成、アドレスサニタイザ付きのビルドとテスト、カバレッジレポートの作成、そしてソースディレクトリを監視して変更時にコマンドを再実行する処理。
## 正しいテストターゲットと、自動で作られるバリアントを見つける
複数のテストファミリーが共存しており、それぞれに独自のエントリーポイントがある。
- 設定駆動の統合テスト。埋め込みスクリプトモジュール、互換性日付、機能フラグ、およびサービスバインディング・構造化データ・キーバリューストア・ステートフルオブジェクトの名前空間といったバインディングを備えたサービスを宣言する、シリアライズされた設定ファイル。
- 専用マクロによってビルドされるネイティブのユニットテスト。ファイル名にはハイフン付きのテスト用サフィックスが付く。
- ランタイム互換性スイートおよびWeb標準適合性スイート。いずれも名前を指定して実行する。
- ベンチマーク。パスを指定して実行する。
ビルドコマンドとテストコマンドはいずれも明示的なターゲットを受け付け、既定ではツリー全体を対象とする。ターゲット名には必須のサフィックスマーカーが付き、ビルドマクロは各テストについて複数のバリアントを自動生成する。すなわち、最も古い互換性ベースラインに固定された既定バリアント、すべてのフラグを有効にした最新日付のバリアント、そしてすべての一時的なゲートを有効にしたバリアントである。あるファイルに対応する正しいターゲットを見つけるには、同じディレクトリのビルドファイルを読むか、そのパッケージ内の全ターゲットをビルドシステムに問い合わせること。
## コードの主な場所をつかみ、似た既存の実装をまねる
編集の前に全体の見取り図を作り、新しい作業では必ず類似する既存実装を見つけてそのパターンに従うこと。
- ランタイムAPI: スクリプトへ公開されるインターフェースのネイティブ実装、および標準ライブラリ互換レイヤーとそれらのテスト。
- I/Oサブシステム: ステートフルオブジェクトのストレージとキャッシュ、リクエスト追跡、リソース制限、スレッド処理、ワーカーのライフサイクル。リクエストごとのコンテキストオブジェクトがリクエスト全体を束ねる。
- グルーレイヤー: エンジンバインディング、型ラッパー、プロミス処理、メモリ管理、モジュールシステム。
- サーバーレイヤー: バイナリのエントリーポイントと設定処理。スキーマファイルで記述されたケイパビリティベースのセキュリティモデルを用いる。
- 互換レイヤー、代替スクリプトランタイム、システム言語統合コンポーネントのための、言語ごとのツリー。
互換レイヤーは二重構造である。ネイティブ側は一方のツリーに、スクリプト側はもう一方のツリーに存在し、新しいモジュールは所定のモジュールテーブルに登録しなければならない。型定義はネイティブの型情報と手書きの宣言から導出され、常に再生成されるものであって、手で編集してはならない。
## 書き方の決まりを守り、コミットは小さく、1つずつテストが通る形にする
参照先のハウススタイルガイドに、そこに記載された例外も含めて従うこと。プロジェクトが意図的にそのガイドから逸脱している箇所では、その逸脱こそが規則である。
- 固定された言語標準、本プロジェクト独特のソースファイル拡張子、ハイフン付きのテストファイル用サフィックスに正確に合わせること。
- 1つのフォーマットコマンドが全言語のフォーマッターをまとめて実行する。変更を提出する前に、このコマンドと全テストコマンドを実行すること。
- コミット前チェックが、ステージされたコード内のデバッグ出力マクロをブロックし、フォーマットチェックを再実行する。これを迂回しないこと。
- 変更は小さなコミットに分割すること。各コミットは単独でコンパイルが通り、テストに合格しなければならない。修正用の後追いコミットを残さないこと。
- スクリプト側のソースは厳格に型付けされている。オプショナルプロパティの厳密なセマンティクス、プライベートフィールド構文、明示的な戻り値型が強制される。
## 標準ライブラリではなく、プロジェクト独自の基盤ライブラリの型を使う
本コードベースは、大半の型について言語の標準ライブラリではなく独自の基盤ライブラリを意図的に使用している。標準のコンテナに手を伸ばすのではなく、周囲のファイルに合わせること。
適用すべき置き換えは次のとおり。標準の文字列の代わりに所有権を持つ文字列と所有権を持たない文字列ビュー、標準のベクタの代わりに固定長配列と可変長ベクタ、標準のスマートポインタの代わりに一意所有・参照カウント・スレッドセーフな参照カウント・弱参照の各所有ハンドル、optional の代わりに maybe 型、標準の関数オブジェクトの代わりに呼び出し可能ラッパー、variant の代わりにタグ付き共用体、生のスパンの代わりに配列スライス、ライブラリの例外型、そして標準のフューチャの代わりにライブラリのプロミスおよびフォーク済みプロミス。
安全性のため、生ポインタよりも配列スライスを優先し、参照が所有者のライフタイムに束縛される場合は、素の参照よりもライフタイムに束縛されたハンドルを優先すること。エラー処理では、maybe を取り出すには専用マクロを使い、失敗が適切な例外型を伴うスクリプト例外として表面化しなければならない場合はスクリプト向けの要求マクロを使い、内部の不変条件にはネイティブのアサーションおよび事前条件マクロを使うこと。
## リークや見えにくい壊れ方を招く、禁止の書き方を避ける
以下は絶対である。違反すると、検出しにくい破損、リーク、あるいは互換性の破壊を招く。
- エンジンのローカルハンドルやグローバルハンドルを、値型の構造体宣言の中に置いてはならない。用意されている参照ラッパーを使うこと。
- エンジンのハンドルをプロミス型の内部に置いてはならない。また、エンジンロックを非同期コルーチンへ渡してはならない。
- 所有ラッパーを介さずに、I/Oオブジェクトへのスクリプトヒープ上の参照を保持してはならない。スコープガードがこれを強制する。
- インスタンスプロパティよりもプロトタイププロパティを優先すること。インスタンスプロパティはガベージコレクションの最適化を無効にしてしまう。
- ユーザー提供のコンテンツをディープフリーズしてはならない。循環値に対して安全ではない。
- 未知の名前をリモートへディスパッチするインターフェースに、互換性フラグでゲートせずにメソッドを追加してはならない。また、シリアライズされる列挙値を変更してはならない。
- 生のタスクリストアクセサよりも、追加型のタスク登録呼び出しを優先すること。また、真偽値パラメータは避け、強い型付けの真偽型を使うこと。
- スクリプトヒープから到達可能なネイティブオブジェクトに格納される参照は、必ずガベージコレクションの走査を実装しなければならない。ネイティブ側の参照循環は決して回収されない。専用のlintチェックがこれを検証する。
- 組み込みデータベースの誤用エラーは常にスローされ、決して握りつぶされない。ステートフルオブジェクトのデータベースではトランザクションは許可されない。
- モジュールの評価はリクエストコンテキスト内で実行してはならない。また、グローバルスコープでの非同期I/Oは禁止されている。
- 共有される型宣言ファイルでは、汎用的なアンビエント型名を使ってはならない。必ず製品名または機能名を接頭辞として付けること。型として素の型なしオブジェクトを使ってはならない。既知のフィールドを持つ適切なインターフェースを宣言し、必要に応じて将来の互換性のためにオープンなレコード型を継承すること。
- 生成されたスナップショットを手で編集してはならない。生成元のレイヤーを修正して再生成すること。
## 動きの変更は、互換性フラグか一時的なゲートの後ろに入れる
本プロジェクトは強い後方互換性のコミットメントを掲げている。いったんデプロイされた挙動は、削除することも黙って変更することもできない。したがって挙動の変更はゲートの背後に導入しなければならず、ゲートは恒久性に応じて選択する。
- 日付駆動の互換性フラグはインスタンス単位で恒久的であり、注釈にフラグ名と有効化日を持つスキーマの中で定義される。すべてのフラグは、その有効化日が到来する前に文書化されていなければならない。
- 一時的なプロセスレベルのゲートは設定駆動であり、条件付き有効化を伴うリスクの高い段階的展開を想定している。これらは列挙型と文字列マップの組で定義され、両者は常に同期していなければならない。
非標準のインターフェースには高い基準を課すこと。プロジェクト固有の挙動を発明するよりも、公開された標準を実装することを優先する。
## ローカルのパッチを保ったまま、取り込んだ依存関係をアップグレードする
サードパーティの依存関係は、ビルドシステムのモジュール機構によって外部ディレクトリへベンダリングされる。統合の設定はモジュールマニフェストと依存関係用のビルドディレクトリに置かれる。旧来のワークスペースファイルがまだ残っている場合もあるが、これはもはや主要な機構ではない。ベンダリングされた基盤ライブラリのヘッダーや名前空間について疑問があるときは、推測せずにそのソースを直接参照すること。
いくつかの依存関係には、ビルド時に適用されるローカル管理のパッチセットが付随している。これらのパッチはプロジェクト固有のカスタマイズや新しいインターフェースを導入するため、依存関係をアップグレードするたびに見直して更新すること。また、本プロジェクトは既定とは挙動の異なる代替メモリアロケータを使用している点にも注意し、メモリ解析ではその点を考慮しなければならない。
エンジンや依存関係のメジャーアップグレードを行う場合は、着手前に、正確な対象バージョンと、保持すべきパッチやカスタマイズを確認すること。そのうえで、文書化された同期、パッチ適用、リベース、パッチ再生成、バージョン更新の各手順に従う。マージコンフリクトは頻繁に発生し、プロジェクト固有の変更を損なわずに保つにはほぼ必ず人間の判断が必要になる。人間のレビューなしに自動的に解決してはならない。全テストスイートが通ることを確認し、遭遇した新しい非推奨事項があれば報告すること。
## あちこちにあるドキュメントを探し、間違いには直す案を出す
ドキュメントは中央集権ではなく分散している。ドキュメント用ディレクトリは、環境構築、新しいインターフェースを追加する際のガイドライン、依存関係のアップグレード手順といった横断的な話題を扱い、一方で個々のソースディレクトリはしばしば、サブシステムの詳細を記した独自のreadmeやガイドを持つ。不慣れな領域で作業する際は、推測したり質問したりする前に、率先してこれらを探すこと。
ドキュメントが欠けている、不正確である、あるいは古くなっている場合は、黙っているのではなく更新を提案すること。まず事実を検証し、その正確性をメンテナに確認せずにドキュメントの編集をコミットしないこと。
