tRPCを業務で使うべきではないと思う理由

はじめに Web APIの開発手法としてはREST API, GraphQLあたりがメジャーな手法かと思いますが、2021年ごろからtRPCが話題になってきました。 特長としてはこんな感じです。 バックエンドのTypeScriptの型定義をフロントエンドにそのまま持ち込める スキーマと実装が乖離する心配がない スキーマ生成の手間がない RESTとGraphQLのいいとこどりをしている RESTと違って取得系APIのパラメータにJSONが使える GraphQLと違ってドメインモデルを反映したグラフの構築が不要で高度なキャッシュ機構もないので、学習コストが低い GraphQLと違って取得系APIがデフォルトでGETメソッドなので、CDNなどでキャッシュできる 実に素晴らしいフレームワークに見えますが、個人的には一部のケースを除いて業務で使うべきではないと考えています。この記事では、 使うべきでないと考えている理由 使えると考えているケース について述べていきます。 使うべきでないと考えている理由 tRPCの大きな特徴として、REST (OpenAPI) やGraphQLと異なり、間に挟まるスキーマが存在せず、TypeScriptの型定義を直接共有しているという点があります。 間に挟まるスキーマがなくなることで、以下のようなメリットがあります。 スキーマと実装が乖離する心配がない スキーマ生成の手間がない スキーマの都合で型情報が失われない 一方で、スキーマがないことは以下のようなデメリットも生みます。 TypeScript以外の言語を使うことが難しくなる フロントエンドとバックエンドが密結合になる ここで、これらのメリット/デメリットを比較してほしいのですが、メリットよりデメリットの方が影響の範囲が大きく、致命的です。メリットは使用するライブラリやコーディング規則さえあればRESTやGraphQLでも代替できるレベルのものですが、デメリットの方は開発体制全体が狭い範囲に縛られてしまい、脱却も難しいものです。メリットとデメリットについて1つずつ説明していきます。 スキーマと実装が乖離する心配がない 拙い実装が行われているサーバーでは、スキーマと実装が乖離するということが往々にして起こります。例えばnullableでないフィールドがnullになるとか、スキーマに存在しない(返してはいけない)フィールドが返るとかです。 しかし、これは実装が悪いのであって、スキーマが悪いわけではありません。 スキーマと実装の乖離は、スキーマからコードを/コードからスキーマを自動生成することである程度回避することができます。高品質な自動生成ツールを選定し、スキーマと実装が乖離するようなコードを書かなければ、スキーマと実装が乖離する可能性は十分低くできます。 スキーマ生成の手間がない スキーマ生成にコマンドを叩く必要があったとしても、一般にビルドなどよりずっと高速に回りますし、ビルドステップなどに組み込めるケースも多いです。スキーマ生成忘れなどはCIで回避できます。 スキーマの都合で型情報が失われない スキーマでサポートされていない型があるとすれば、その型はある程度特殊な型ということであり、そもそもそのような型を使わないということを検討する必要があります。 例えば、複雑なユニオン型はOpenAPIでもGraphQLでも完全にはサポートされていませんが、そもそもそのようなユニオン型を返すよりも、型ごとに別のフィールドで返すようにした方が筋がいいことが多いです。型定義に条件分岐が入っているような場合は、そのようなフィールドを別の型に切り出すなどの対応を考えましょう。 TypeScript以外の言語を使うことが難しくなる フロントエンドもバックエンドもTypeScriptに縛られます。TypeScript以外でも使うことはできますが、それなりのペインを伴います。 フロントエンド 例え最初はWebだけで提供する想定だったサービスでも、サービスが拡大してモバイルアプリなどを提供することになる可能性というのは十分考えられます。 アプリケーションがtRPCを使っているとなったら、モバイルアプリはTypeScriptで書けるフレームワークを使うか、スキーマから各言語の型を再構築するかという2択を迫られます。 モバイルアプリで使うフレームワークとしてはOSネイティブ、Flutter、React Nativeなどがありますが、開発体制や要件などにより適した技術は変わるため、これが制限されると開発やパフォーマンスなどに負の影響が出てしまいます。 ZodスキーマからSwiftコードへのジェネレーターなど、自動でネイティブアプリ向けのスキーマを生成する試みもないわけではないですが、2024年現在ライブラリとして見つかるのはこれくらいで、今後増えていくかもわかりません。 手動でスキーマから型を再構築するとなると、それこそスキーマとの乖離が問題になってきます。 バックエンド Ruby on RailsからScalaに移行したTwitter、JavaからNodeに移行したPayPalなど、開発体制やパフォーマンスの観点からバックエンドを移行するというのは、頻繁ではありませんが選択肢には入れておきたいです。 APIスキーマが存在している場合、バックエンドアプリケーション/API/フロントエンドアプリケーションという全体のアーキテクチャが維持されており、APIが適切に設計されていれば、バックエンドをAPIに合わせて書き直すだけで段階的に移行することができます。 ところが、tRPCを採用している場合、これらのケースにおいてAPIの移行もほぼ必須になってしまい、フロントエンドのコードも書き換える必要が生じます。 フロントエンドとバックエンドが密結合になる フロントエンドとバックエンドの間にスキーマという緩衝材がないので、一体的に開発する必要が生じます。 このため、フロントエンド担当とバックエンド担当といったように分業することが困難になります。エンジニア単位ならともかく、ベンダー単位での分業はかなり難しくなります。 使えると考えているケース ここまでスキーマレスのデメリットがいかに致命的かということを説明してきました。 逆に言うと、ここまでのデメリットが気にならないようなケースであれば、手軽に使えるtRPCは非常に有用なツールになります。つまり、次のようなケースです。 TypeScript以外の言語は使えなくてよい フロントエンドとバックエンドが密結合でよい このことは、T3 Stackの提唱者であるTheo Browne氏の以下の図でも説明されています。 https://hackernoon.com/the-simplicity-of-trpc-with-the-power-of-graphql では、このようなケースは具体的にはどのようなものがあるのでしょうか。 フロントエンド-BFF間通信 フロントエンドとバックエンドの間にBFFを置く場合はtRPCが有用です。BFFはフロントエンドと密結合になるのが普通なので、これが問題にはなりません。また、BFFはフロントエンドの担当が触れた方がスムーズですし、Next.jsなどのSSRフレームワークをBFFとして使用する場合、言語は要件的にTypeScript一択になります。 拡張予定のない小規模アプリケーション フロントエンドとバックエンドをともにTypeScriptで一体開発し、参画するエンジニアも多くなく、システムの拡張性を一切考えないのであれば、tRPCを使ってもいいと思います。T3 Stackで作るのもいいですね。...

<span title='2024-03-10 00:00:00 +0000 UTC'>3月 10, 2024</span>&nbsp;·&nbsp;antenna-three

人はなぜガチャで爆死するのか ~ソシャゲに潜む認知バイアス~

はじめに ソシャゲをある程度プレイしている方ならガチャで理不尽な目にあった経験はあるでしょう。よほど問い合わせが多かったのか消費者庁が確率の授業をする羽目になったりしていますが、こういった説明をされてもなお腑に落ちないという人も多いのではないのでしょうか。 こういった現象は認知バイアスによって説明できます。認知バイアスは人が事象を解釈するときに持つ歪みのことです。この記事ではガチャで感じやすい理不尽について、認知バイアスを使った説明を行います。こういった認知バイアスを知っていると、自分の状況を客観的に判断して、ガチャを引くかどうかや撤退するかどうかの意思決定をより合理的にできます。 ガチャの高レアが出ない ソシャゲをプレイしている人の多くはガチャの高レア排出率が表記されている確率より低いように感じているのではないでしょうか。確かに統計を取ってみると正しいのだけれど、どうもちゃんと出ているようには思えない……。 これはプロスペクト理論の確率加重関数で説明ができます。 プロスペクト理論というのは雑に説明すると「人間の感じる価値には偏りがある」というもので、確率加重関数は人間の感じる確率の歪みを表します。確率加重関数はこんな感じの関数です。 このグラフを見ると「低い確率は過大評価され、高い確率は過小評価される」ことがわかります。具体的な値についてはいろんな理論があるので一概には言えませんが、主なソシャゲの高レア排出率である10%未満の確率はかなり過大評価されるのです。 そろそろ当たらないとおかしい 何百連も回して目当ての高レアが出ないと、もうすぐ当たるのではないかと期待してずるずると引き続けてしまうことはありませんか? これはギャンブラーの誤謬と呼ばれる認知バイアスによるものです。ギャンブラーの誤謬は、ある事象が続けて起きた後はその事象が起きにくくなる(例えばコイントスで10連続で表が出たら次は裏が出やすくなる)という推論のことです。 実際にはガチャはランダムかつ独立な試行なので、このような推論は間違っています。つまり、どれだけ外れが続いたところで出やすくなるなんてことはないのです。 出るモードと出ないモードがある 確かにトータルで見たら表記されている確率通りかもしれないけど、数百連の間全くでなかったり急にいっぱい出たりして偏りが激しい、射幸心をあおるために出るモードと出ないモードを切り替えているのでは?という意見も見かけることがあります。 こういった認知を生み出しているのはクラスター錯覚です。クラスター錯覚はランダムな分布の中にできるかたまり(クラスター)を見て偏りや法則があると感じてしまう認知バイアスです。 例として、正方形の中にランダムに点を打つ様子を見てみましょう。 点が集まっている領域や空白の領域があるように感じられることでしょう。これを見ると、むしろ点が「平均的に」分布している領域の方が少ないことがわかります。ランダムな分布には必然的に偏りが生まれるのに、人はそれを見てランダムでないように感じてしまうのです。 今は調子いいから引ける 先ほどのクラスター錯覚から出やすいときと出にくいときがあると錯覚し、高レアが出た後は調子がいいのでまた高レアが引けると結論付ける場合があります。これはギャンブラーの誤謬とは逆の働きの認知バイアスですが、こちらにもホットハンドの誤謬という名前が付いています。ガチャをたくさん引いてきた人ほどホットハンドの誤謬に陥りやすいです。 ここまで来たら撤退なんてできない 外れが続くと意地になって無理に課金してでも当たりが出るまで回してしまうことはありませんか?ここで撤退したら今までの課金が無駄になると思っていませんか?それはサンクコストバイアス、あるいはコンコルド効果と呼ばれる認知バイアスです。 今までの課金はその後当たりが出ようが出まいがもう取り戻すことができません。つまり、さらに課金するかどうかの意思決定に今までいくら課金したかは全く関係ありません。しかし、人はそれまでの課金額に気を取られて、ここでやめるのはもったいないと考えてしまうのです(実際にはお金はガチャに突っ込んで爆死した時点で無駄になっています)。 有償石だと排出率が低い 無償石だとすぐに高レアが出るのに、有償石だと全然高レアが出ない。課金させるために有償石からは出にくくしているのでは?という疑いも見かけることがあります。このような錯覚をしてしまう理由としてメンタルアカウンティングがあります。 メンタルアカウンティングはお金に対して入手方法や使いみちによって「色」を付けてしまい、別の勘定として扱う心の動きのことです。例えばギャンブルで手に入れたお金は、働いて手にしたお金より浪費してしまいがちです。また、普段10円でも安い食材を買うくせにソシャゲに数万円をポンポン課金してしまうのもメンタルアカウンティングの影響です。 このため、特に微課金の人などは無償石は気兼ねなく使ってしまうので労せず高レアが出るような印象を感じ、有償石に対しては特別な価値を感じるのでせっかく課金したのに出ないという印象を持ってしまうのです。 みんな神引きしてるし自分もいける Twitterを見るとみんな2枚抜きしたり呼符で引いたりしているし、これは自分もサクッと引けるのでは……? これは説明するまでもないかもしれませんが、生存者バイアスです。引きがよかった人はツイートし、引きが良くなかった人はツイートしないので出やすいかのように見えるだけです。 大成功教はガチ 強化で大成功が出た後に回すと当たる、フレンドポイント召喚で星3サーヴァントが出た後に回すと当たる、などなど、さまざまなガチャ宗教がまことしやかにささやかれています。 これは錯誤相関という認知バイアスです。大成功や高レア排出など、確率の低い事象が起きるとそれが強く印象に残るので関係があるように思い込んでしまうのです。 プログラマーのはしくれとして付記しておくと、疑似乱数の性質によって偏りが生じているということもありえないと断言できます。 おわりに ガチャを引くときに陥りやすい認知バイアスについて見てきました。これらの認知バイアスは、確率論などについて勉強してきた人であっても陥ることがあります。バイアスだと分かったうえでネタとして楽しんでいる方もいますが、気付かずにハマってしまうと思わぬ出費につながってしまいます。 こうした事態を避けるためには、 ガチャの排出率を見るだけでなく、確率分布を確認する 追い課金する前に落ち着いて現状を確認し、バイアスを意識する ゲームの予算を設定し、これを必ず守る といった対策が有効です。 これらの対策を講じた結果、私はいつも通り爆死しました。 それでは皆様、よき爆死ライフを。 参考文献 Category:認知バイアス - Wikipedia

<span title='2021-11-16 00:00:00 +0000 UTC'>11月 16, 2021</span>&nbsp;·&nbsp;antenna-three

FGOにおけるドロップ率の区間推定

目次 はじめに 統計学基礎 ドロップ率の区間推定 信頼区間はどれくらい? ドロップ率のZ検定 まとめ 参考文献 はじめに FGOアイテム効率劇場では、ユーザーの報告からFGOのアイテムのドロップ率を集計しています。クエストによっては報告された周回数の合計が小さいことがあり、このような場合にはドロップ率には上振れや下振れが発生する場合があります。そこで、周回数に対してどれくらいの不確定さが発生するのかを知るために、ドロップ率の区間推定を行います。 得られる結果としてはドロップ率に対して±何%くらいの幅がありそうかということが示され、このクエストのドロップ率は他のクエストのドロップ率より高いと言えるのか?といったことがわかります。 また、自分で周回した結果と効率劇場のデータに差があったときに、それは偶然なのか?ということについても述べています。 統計学基礎 今回の内容を理解するために必要な基礎知識について述べています。内容はかなり端折っていますので、正確な内容は参考文献を参照してください。 統計については知っているよ!という方はドロップ率の区間推定まで、数式はいらないから結果だけ見たい!という方は信頼区間はどれくらい?まで飛ばしてください。 区間推定 アイテムのドロップ率はFGOの運営によってある割合に設定されているはずで、これが我々が知りたい真の値(母平均)です。しかし、我々は(不正アクセスでもしない限り)真の値を知ることはできず、周回して集めたデータ(標本)から真の値を推定する必要があります。 標本から求めた平均(標本平均)は真の値とは異なります。しかし、サンプルサイズが大きくなれば、標本平均は母平均に近づくことが期待されます(大数の法則)。そこで、これくらいの範囲にはおそらく母平均があるだろうと推定することを区間推定といい、この区間を信頼区間と呼びます。 区間推定を行うときには、その確かさを信頼水準という数値で決める必要があります。よく使われる信頼水準は95%で、このときの信頼区間を95%信頼区間と呼びます。 正規分布 正規分布は平均を$\mu$、分散を$\sigma^2$として $$ N(\mu, \sigma^2) = \frac{1}{\sqrt{2\pi\sigma^2}}\exp{\left(-\frac{(x-\mu)^2}{2\sigma^2}\right)} $$ という形で表される分布で、さまざまな事象の確率分布として使われています。 正規分布のグラフは下のようになります。 M. W. Toews, CC BY 2.5, via Wikimedia Commons 母平均の区間推定 正規分布$N(\mu,\sigma^2)$からサンプルサイズ$n$、標本平均$\bar{x}$の標本を取り出したとき、母平均の95%信頼区間は大体次のようになります。 $$ \bar{x} - 2 \times \sqrt{\frac{\sigma^2}{n}} \le \mu \le \bar{x} + 2 \times \sqrt{\frac{\sigma^2}{n}} $$ ここで、式の中の$\sqrt{\frac{\sigma^2}{n}}$の部分を標準誤差といいます。95%信頼区間はおよそ標本平均±2×標準誤差、99%信頼区間はおよそ標本平均±3×標準誤差です。 二項分布 結果が成功か失敗かの2通りであるような試行をベルヌーイ試行といいます。成功確率が$p$であるベルヌーイ試行を$n$回行って成功した回数の分布を二項分布$B(n, p)$といいます。 母比率の区間推定 さて、ベルヌーイ試行を$n$回行い、標本比率(成功回数/試行回数)が$\hat{p}$だったとき、$p$の真の値(母比率)を区間推定することを考えます。 $n$を増やすと、二項分布$B(n, p)$は正規分布$N(np, np(1-p))$に近づくことが知られています。 これを母平均の区間推定の式に代入して各辺を$n$で割ることで、母比率の95%信頼区間は次のようになります。 $$ \hat{p} - 2 \times \sqrt{\frac{\hat{p}(1-\hat{p})}{n}} \le p \le \hat{p} + 2 \times \sqrt{\frac{\hat{p}(1-\hat{p})}{n}} $$...

<span title='2021-11-03 00:00:00 +0000 UTC'>11月 3, 2021</span>&nbsp;·&nbsp;antenna-three