Control Resonantの記事を読んで、思ったより深く刺さった。
ゲームの話なんだけど、Remedy Entertainment のクリエイティブディレクターの Mikael Kasurinen が言ってた「どの順番でプレイしてもいい」という設計思想が、なんかずっと頭に引っかかってる。2019年の前作 Control の主人公 Jesse Faden の物語はもう終わってる。でも続編の Resonant は「次に何が起きたか」じゃなくて、Jesse の弟 Dylan を通じて同じ世界を別視点で描く作りになってる。前作を知らなくてもゲームに入れるし、知ってたらより深く楽しめる。
これ、API 設計の話じゃん、と思った。
最近、社内の別チームが使う認証フローの一部をリファクタリングした。元のコードは「最初からこの順番で呼ばないと死ぬ」構造になってた。session を init してから token を fetch して、その返り値を次の関数に渡して...という依存の連鎖。途中からテストを書こうとすると、全部 mock しないといけなくて地獄だった。ハマった。2日消えた。
そのとき「どこからでも入れる設計」って何だろうと考え始めた。
Kasurinen のコメントで印象的だったのは「Jesse は現実世界から超常世界に入っていく。Dylan はずっと超常世界にいた側で、今度は現実世界が新鮮に映る」という話。視点が逆なのに、どちらも有効なエントリーポイントになってる。この非対称性、モジュール設計でも使える発想だと思った。
自分がいま個人開発で触ってる小さな CLI ツールで、試しにこの考え方を適用してみた。やったのはシンプルで、コアのロジック層が外の状態に依存しないように境界を引き直した。
たったこれだけでテストが書きやすくなった。fetch_token を mock しなくても process_data 単体で assert できるようになった。「前提なしでも入れる層」を明示的に作る、という感覚がやっとつかめた気がした。
Control Resonant の Dylan は「超常世界についての知識はある、でも現実世界のルールは知らない」というキャラクター設定で、彼がプレイヤーに世界観を説明する立場になる。これって依存関係の逆転に似てる。知識を持ってる側がインターフェースになることで、知らない側がスムーズに入ってこれる構造。
チーム開発でも同じことが起きる。ライブラリを深く理解してる人が、知らない人向けのラッパーを書く。でもそのラッパーが「知ってる前提」で書かれてると、結局誰も得しない。「どの順番で呼んでも壊れない」か「間違った呼び方をしたら即エラーになる」か、どちらかに振り切る必要がある。中途半端な暗黙の前提が一番えぐい。
彼女に「なんでゲームの記事読んで仕事のこと考えてるの」と言われた。確かに。でも Remedy の「世界という器はひとつ、レンズは複数」という設計思想はマジで刺さった。
フロントエンドでも Redux とか Zustand を使うとき、「store の初期状態がどうあれ、どの画面からマウントされても壊れない」設計を意識するかどうかで、後からの改修コストが全然変わる。自分が今のチームに入ったとき、誰かの書いた store が「TOPページから入ること前提」になってて、詳細ページを直リンクで開くと壊れる実装に出くわした。あのときの絶望を今でも覚えてる。
Resonant の発売は来年以降だと思うけど、プレビューを読んで「続編だけど入口でもある」という設計を実際に 2 時間以上プレイした人のレポートで確認できたのはよかった。Kasurinen が言う「世界の物語で、ゲームごとに別のレンズで覗く」という言葉、API バージョニングや microservices の境界設計を語るときに使いたいフレーズだと思った。
次の設計レビューで使ってみる。「このモジュール、どこからでも入れますか?」って聞いてみる。
ゲームの話なんだけど、Remedy Entertainment のクリエイティブディレクターの Mikael Kasurinen が言ってた「どの順番でプレイしてもいい」という設計思想が、なんかずっと頭に引っかかってる。2019年の前作 Control の主人公 Jesse Faden の物語はもう終わってる。でも続編の Resonant は「次に何が起きたか」じゃなくて、Jesse の弟 Dylan を通じて同じ世界を別視点で描く作りになってる。前作を知らなくてもゲームに入れるし、知ってたらより深く楽しめる。
これ、API 設計の話じゃん、と思った。
エントリーポイントが複数ある設計を意識するようになった経緯
最近、社内の別チームが使う認証フローの一部をリファクタリングした。元のコードは「最初からこの順番で呼ばないと死ぬ」構造になってた。session を init してから token を fetch して、その返り値を次の関数に渡して...という依存の連鎖。途中からテストを書こうとすると、全部 mock しないといけなくて地獄だった。ハマった。2日消えた。
そのとき「どこからでも入れる設計」って何だろうと考え始めた。
Kasurinen のコメントで印象的だったのは「Jesse は現実世界から超常世界に入っていく。Dylan はずっと超常世界にいた側で、今度は現実世界が新鮮に映る」という話。視点が逆なのに、どちらも有効なエントリーポイントになってる。この非対称性、モジュール設計でも使える発想だと思った。
「前提なしでも動く層」を明示的に分ける
自分がいま個人開発で触ってる小さな CLI ツールで、試しにこの考え方を適用してみた。やったのはシンプルで、コアのロジック層が外の状態に依存しないように境界を引き直した。
# before: 呼び出し順が暗黙の前提になってた
init_session
fetch_token
process_data $TOKEN
# after: 各関数が自分の入力だけに依存
TOKEN=$(fetch_token "$CREDENTIAL")
process_data "$TOKEN" "$INPUT"たったこれだけでテストが書きやすくなった。fetch_token を mock しなくても process_data 単体で assert できるようになった。「前提なしでも入れる層」を明示的に作る、という感覚がやっとつかめた気がした。
Control Resonant の Dylan は「超常世界についての知識はある、でも現実世界のルールは知らない」というキャラクター設定で、彼がプレイヤーに世界観を説明する立場になる。これって依存関係の逆転に似てる。知識を持ってる側がインターフェースになることで、知らない側がスムーズに入ってこれる構造。
チーム開発でも同じことが起きる。ライブラリを深く理解してる人が、知らない人向けのラッパーを書く。でもそのラッパーが「知ってる前提」で書かれてると、結局誰も得しない。「どの順番で呼んでも壊れない」か「間違った呼び方をしたら即エラーになる」か、どちらかに振り切る必要がある。中途半端な暗黙の前提が一番えぐい。
状態管理と入場順の話
彼女に「なんでゲームの記事読んで仕事のこと考えてるの」と言われた。確かに。でも Remedy の「世界という器はひとつ、レンズは複数」という設計思想はマジで刺さった。
フロントエンドでも Redux とか Zustand を使うとき、「store の初期状態がどうあれ、どの画面からマウントされても壊れない」設計を意識するかどうかで、後からの改修コストが全然変わる。自分が今のチームに入ったとき、誰かの書いた store が「TOPページから入ること前提」になってて、詳細ページを直リンクで開くと壊れる実装に出くわした。あのときの絶望を今でも覚えてる。
Resonant の発売は来年以降だと思うけど、プレビューを読んで「続編だけど入口でもある」という設計を実際に 2 時間以上プレイした人のレポートで確認できたのはよかった。Kasurinen が言う「世界の物語で、ゲームごとに別のレンズで覗く」という言葉、API バージョニングや microservices の境界設計を語るときに使いたいフレーズだと思った。
次の設計レビューで使ってみる。「このモジュール、どこからでも入れますか?」って聞いてみる。