


レイヤー付き画像ファイルを、データを失わずに読み書きするライブラリを作る
レイヤー付き画像のバイナリ形式を扱うライブラリを作る・直す時に使う。低レベルの読み書きと利用者向けAPIを分け、セクションを順番どおりに読み、未知のデータを残し、レイヤーのグループを組み立て直し、読む・書く・読み直すテストで確かめ、自動の手順でリリースする。
このAIルールを使うには
## 作業に要る依存関係だけを入れ、プロジェクトのコマンドで作業する
コードを変更する前に、コントリビューター向け文書を読む。
無関係なランタイムからツールを直接起動せず、リポジトリの環境マネージャーとタスクコマンドを使う。
タスクに必要な依存関係グループだけをセットアップする。
- パースとテストには、標準の開発用依存関係をインストールする。
- 合成またはラスタライズを扱う場合は、任意のレンダリンググループを追加する。
- 文書をビルドする場合に限り、文書用グループを含める。
- 新しいランタイムとOSの組み合わせによっては、任意の科学計算用または描画用パッケージを利用できないことを想定する。
標準コマンドを使って、次を行う。
- コンパイル済み拡張モジュールと配布用成果物をビルドする。
- カバレッジ付きで全テストスイートを実行する。
- ノード識別子を指定して単一テストを実行する。
- 対象を絞った挙動を測定する場合は、カバレッジなしでテストを実行する。
- lint、フォーマット、静的型チェックを実行する。
- HTML文書をビルドし、文書に記載された出力ディレクトリを確認する。
任意のレンダリング依存関係を、パースの中核要件として扱わない。
依存関係がない状態で任意機能を求められた場合は、明確なインストール手順を示す。
## 低レベルのバイナリの読み書きと、利用者向けAPIを分けておく
低レベルのバイナリパースと高レベルのユーザーAPIを分離しておく。
低レベル層は、次を担う。
- 仕様に従って生のバイナリ形式を読み書きする。
- ヘッダー、レコード、タグ付きブロック、ディスクリプター、画像リソース、マスク、チャンネル、圧縮データをモデル化する。
- 明示的なシリアライズメソッド `read(fp)` と `write(fp)` を公開する。
- まだ解釈できない未知のデータを保持する。
高レベル層は、次を担う。
- 低レベルの文書構造を扱いやすいインターフェースでラップする。
- 平坦なバイナリレコードから、ユーザー向けのオブジェクトツリーを再構築する。
- レイヤー種別、エフェクト、マスク、レンダリング、文書操作を公開する。
- 契約上許される場合は、データへアクセスされるまで高コストなパースを遅延する。
内部コードでは、低レベルの具体的な定義を、その定義元モジュールからインポートする。
外部利用者は、サポート対象のパッケージエクスポートを使う。
互換性上の理由がない限り、低レベルの表現詳細を公開メソッドへ漏らさない。
## ファイルのセクションを順番どおりに読み書きし、未知のデータを残す
セクション順序、長さ、シグネチャ、数値幅を、ファイル形式契約の一部として扱う。
一般的な階層画像ファイルは、次の5つの連続したセクションで構成される。
1. シグネチャ、バージョン、寸法、チャンネル、カラーモードを含む固定サイズのファイルヘッダー。
2. インデックスカラーパレットなどのカラーモードデータ。
3. カラープロファイル、ガイド、サムネイルなどの画像リソース。
4. レイヤーレコード、チャンネルデータ、タグ付きブロックを含むレイヤーおよびマスク情報。
5. フラット化された合成画像データ。
仕様で求められる場合は、26バイトなどの固定ヘッダーサイズを検証する。
セクションは順番どおりに読み書きし、扱いやすいメモリ内オブジェクトから境界を推測しない。
呼び出し元がサイズ計算を検証できるように、シリアライズメソッドから書き込んだバイト数を返す。
未知のペイロードを破棄せず、往復処理中はバイト列のまま保持する。
## 平らに並んだレコードから、レイヤーのグループを組み立て直す
形式が階層を平坦なリストとして保存する場合は、隣接関係だけを仮定せず、明示的な境界レコードを解釈する。
グループ開始・終了マーカーを認識し、スタックまたは同等の検証済み状態機械を使って親子関係を再構築する。
少なくとも次をテストする。
- グループ外にある通常レコード。
- 複数の子を含む1つのグループ。
- 入れ子のグループ。
- 空のグループ。
- 不正または対応の取れていない境界マーカー。
- 元のレコード順序の保持。
高レベルAPIではツリーを公開しつつ、シリアライズ用に低レベルの平坦なレコードリストを維持する。
ツリーの再構築によって、無関係なバイナリレコードを変更しない。
## タグ付きブロックをレジストリで増やし、未対応のブロックはそのまま残す
バイナリ構造には、クラスレベルの読み取りとインスタンスレベルの書き込みを備えた共通の基底契約を与える。
小さなプリミティブから複雑な構造を再帰的に組み立てる。
型付きデータクラス機能を、次の用途で一貫して使う。
- 生成される初期化処理。
- フィールド検証。
- 型ヒント。
- 制御された表現。
- 必要な場合の予測可能なタプルまたはシーケンスへの変換。
バリデーターをアノテーションと一致させ、流れるような呼び出しやクラスを認識するコンストラクターでは、同一インスタンス型の戻り値を使う。
拡張可能なタグ付きブロックでは、次を行う。
- 固定幅のタグキーと、それに対応するペイロードを使う。
- レジストリを通じて既知のタグをハンドラークラスに対応付ける。
- 型付きデータを読み取る前に、タグの有無を確認する。
- 未対応のブロックを、後で書き戻せるようそのまま保持する。
- 公開用の便利なアクセスと内部レコードへのアクセスを分離する。
忠実に書き戻せないバイトを消費するハンドラーは登録しない。
## 圧縮やピクセル計算を速くしても、フォールバックは壊さない
性能が重要な処理経路は、最適化する前に測定する。
高コストな圧縮または展開には、利用可能であればコンパイル済みコーデックを使い、正しい純粋言語実装のフォールバックを維持する。
遅延パースで意味が保たれる場合は、マスク、エフェクト、チャンネルデータに遅延読み込みを使う。
大規模なピクセル計算やブレンドモード計算では、スループットが向上する場合に配列のベクトル化を使う。
すべての最適化で、次を維持する。
- バイナリ出力の互換性。
- エラー時の挙動。
- 決定論的な結果。
- 最適化されていないフォールバック。
- コンパイル済みまたは任意の依存関係を利用できない環境への対応。
最適化実装とフォールバック実装を、同じフィクスチャに対してテストする。
速度を得るために、結果を暗黙に変更しない。
## 読む・書く・読み直すテストで変更を確かめ、型ヒントを付ける
テストをパッケージ構造に対応させて整理し、低レベルのバイナリテストと高レベルのAPIテストを分離する。
代表的なバイナリフィクスチャを専用のフィクスチャディレクトリに置き、複数ファイルを対象にパラメーター化する。
往復検証を主要な不変条件として使う。
1. バイナリファイルを読み取る。
2. 制御されたフィールドを変更する。
3. ファイルを書き込む。
4. 結果をもう一度読み取る。
5. 意図した変更と、無関係なデータが保持されていることを検証する。
新しい関数のすべてのシグネチャに、適切な型ヒントを追加する。
データクラスのバリデーターをアノテーションと一致させる。
自身のクラスのインスタンスを返すメソッドには、プロジェクトの互換型を使う。
開発中は対象を絞ったテストを実行し、完了前に関連する全テストスイート、lint、フォーマット、型チェックを実行する。
カバレッジ率だけを正しさの証明にせず、カバレッジの変化をレビューする。
## 自動のリリース手順で出し、対応していない機能ははっきり伝える
最終版、プレリリース版、ポストリリース版の形式を含め、プロジェクトで文書化されたバージョニング標準に従ってリリースバージョンを選ぶ。
前回のタグ以降の変更を、新しいバージョン見出しの下にある変更履歴へ要約する。
自動タグ付けに必要なリリースブランチのパターンを正確に使う。
メインブランチに対するリリースリクエストを作成し、承認後にのみマージする。
設定済みの自動処理に、マージされた正確なコミットへのタグ付け、配布物のビルド、ホストされたリリースの作成、パッケージレジストリへの発行を任せる。
リリースワークフローがタグ付けを担う場合は、手動でタグを作成しない。
自動処理の認証情報には、後続ワークフローの開始に必要な最小限の権限だけを与え、決して漏らさない。
機能が動くと主張する前に、次について文書化された制限を確認する。
- フォントエンジンがない場合のテキストレンダリング。
- 対応済みおよび未対応の調整処理。
- 実装済みレイヤーエフェクトの範囲。
- 埋め込みオブジェクトの抽出と編集の違い。
- 未知のデータを解釈せずに保持すること。
未対応の挙動は、暗黙に近似せず明示する。
最後に、実行したテスト、試していない任意経路、互換性リスク、依頼された変更へ影響する制限を報告する。
