代表的な面接トピック

コーディング面接: WebAssembly Component Model を使って言語横断コンポーネントをどのように設計しますか?

コーディング難しい
Offer.cc 編集チーム公開日 更新日

質問

あるチームが Rust、Go、JavaScript のコンポーネント間で相互に呼び出しを行いたいと考えています。コアモジュールの ABI を安定した API として扱わずに、WebAssembly Component Model を使ってインターフェースをどのように設計しますか?

プロンプトとコンテキスト

あるプラットフォームで、異なる言語からコンパイルされたプラグインを読み込み、複数のランタイムで合成(compose)する必要があります。WIT でインターフェースを定義し、コンポーネントを合成し、文字列やリソースを処理し、Canonical ABI、エラー、バージョンの進化をテストする方法を説明してください。

面接官が見ているポイント

  • WebAssembly コアモジュール層と Component Model インターフェース層の区別。
  • 言語ニュートラルなインターフェース記述としての WIT と、高水準型のコンポーネント間表現としての Canonical ABI の理解。
  • リソースの所有権(ownership)、借用(borrowing)、result エラー、および非同期ライフサイクルの説明。
  • コンポーネントのバージョン、ケーパビリティの分離、アダプター、ランタイムの違い、および互換性テストの考慮。

確認すべき明確化のための質問

  1. プラグインの境界を越える型は何ですか: 大きなファイル、ストリーム、ハンドル、または長寿命のリソースですか?
  2. ランタイムはどの WASI ケーパビリティを付与しますか?また、プラグインはファイル、ネットワーク、またはキーから分離する必要がありますか?
  3. 呼び出しは同期的ですか、それとも非同期ですか?また、エラー発生時にコンポーネントは回復、リトライ、終了のいずれを行うべきですか?
  4. WIT のバージョンはどのように公開されますか?古いコンポーネントを新しいホストと合成できますか?また、互換性マトリックスは誰が管理しますか?

30秒での回答

私なら WIT で最小限の言語横断コントラクトを記述し、各言語向けのバインディングを生成します。コンポーネントは、特定の言語のメモリレイアウトを公開するのではなく、Component Model インターフェースと Canonical ABI を介して文字列、リスト、result、リソースをやり取りします。ランタイムは必要なファイルやネットワークのケーパビリティのみを付与し、ホストがキャンセル、期限(deadline)、エラーマッピング、クリーンアップを管理します。バージョンの進化には追加的(additive)なフィールドと互換性テストを使用し、コアモジュールの ABI の詳細は実装固有のものとして留めます。

ステップごとの詳細解説

1. コアモジュールとコンポーネントインターフェースの分離

コアモジュールは線形メモリ、関数、数値型を公開します。Component Model はその上に高水準のインターフェース、依存関係、合成を構築します。言語横断 API はコンポーネントインターフェースにとどめるべきであり、コンパイラのメモリアドレス、構造体レイアウト、エクスポートされた関数名を公開コントラクトとして公開してはなりません。

2. WIT で言語ニュートラルな型を表現する

WIT はインターフェース、world、リソース、result エラーを記述し、ツールによって Rust、Go、その他の言語向けバインディングが生成されます。以下の例はリソースハンドルを示しています。対象のツールチェーンで正確な構文を検証してください:

wit
package example:plugin;

interface store {
  resource session;
  open: func(name: string) -> result<session, string>;
  read: func(s: borrow<session>) -> result<list<u8>, string>;
}

インターフェースはビジネスセマンティクスを公開し、ストレージ、スレッド処理、メモリ割り当ては実装の詳細のままとなります。

3. Canonical ABI とリソースのライフサイクルを理解する

Canonical ABI は、文字列、リスト、result、その他の高水準値が WebAssembly コア表現を使用してコンポーネント境界をどのように越えるかを規定します。コンポーネントモデルはリソースの所有権と借用を管理します。ホストはクローズ、キャンセル、クリーンアップを定義する必要があります。数値ハンドルは永続的なポインタではありません。大容量データの場合は、コピー、ストリーミング、バックプレッシャーのコストを評価してください。

4. エラー、ケーパビリティ、バージョンを設計する

想定されるビジネス上の失敗は result 値または明示的なエラー列挙型としてモデル化し、リトライ可能なエラーと認証失敗を区別します。必要な WASI ケーパビリティのみを付与し、実行時間、メモリ、並行性、出力サイズを制限します。可能な限り WIT を追加的に進化させ、古い world の互換性テストを維持し、セマンティクスの変更を隠蔽することなくバージョン変換用のアダプターを使用します。

模範回答

私なら WIT で最小限の言語ニュートラルな world インターフェースを定義し、バインディングを生成します。コンポーネントは Component Model を介して合成され、Canonical ABI が境界における文字列、リスト、result、リソースを表現します。コアモジュールのメモリとエクスポートは実装の詳細として扱います。リソースには借用と明示的なクローズセマンティクスを使用し、ホストがキャンセル、期限、ケーパビリティ、クリーンアップを制御します。エラーには識別可能な result 型を使用し、ケーパビリティは最小権限の原則に従います。WIT の変更ごとに言語横断の互換性マトリックスを実行し、古いコンポーネントが新しいセマンティクスに暗黙的にバインドされないようにアダプターでバージョン変換を処理します。

よくある間違い

  • コアモジュールの線形メモリや言語構造体を直接共有し、コンポーネントインターフェースをバイパスすること。
  • WIT を単一言語のヘッダーファイルのように扱い、生成されたバインディングや型のセマンティクスを無視すること。
  • リソースハンドルを、借用、クローズ、キャンセル、クリーンアップのルールなしに生のポインタとして扱うこと。
  • 正常系(happy path)のみをテストし、result エラー、バックプレッシャー、期限、拒否されたケーパビリティのテストを省略すること。
  • 最小権限の境界を設定せず、プラグインにファイルシステム全体やネットワークへのアクセスを付与すること。
  • WIT の変更後にホストのみをコンパイルし、古いコンポーネントと新しいホストの互換性テストをスキップすること。

フォローアップの質問と回答

バイトリストの代わりにストリームを使用するのはどのような場合ですか?

データが大規模になる可能性がある場合、インクリメンタルに処理する必要がある場合、またはメモリ使用量のピークを制限する必要がある場合にストリームを使用します。バックプレッシャー、完了、キャンセルを定義してください。サイズとライフサイクルを検証した上であれば、小さく制限された設定や結果にはリストを使用できます。

悪意のあるプラグインがリソースを消費するのをどのように防ぎますか?

ランタイムでケーパビリティ、メモリ、実行時間、並行性、出力サイズを制限し、ホストによるキャンセルと分離を提供します。呼び出し時間、エラー率、クォータを監視し、違反が発生した場合は終了させてクリーンアップを実行します。

WIT の変更で互換性を維持するにはどうすればよいですか?

オプションのインターフェースまたはフィールドを優先し、既存の型の意味を維持します。古い world バージョンと言語横断テストを維持し、必要に応じてアダプターを使用します。セマンティクスの削除や変更には、新しいバージョンと移行期限の設定が必要です。

公開情報ソース

関連する質問

関連面接ツール

コーディング問題にはスクリーンショットを使用

問題をキャプチャし、制約条件、解法アプローチ、コード、エッジケース、計算量の順に進めます。

ツールを見る