> For the complete documentation index, see [llms.txt](https://weekly-essay.mikey.wang/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://weekly-essay.mikey.wang/ja/2026/w03-lessons-from-advanced-troubleshooting.md).

# W03-難しい問題の調査から得たこと

3年近く悩まされてきた、Webpackの幽霊のような問題に、先週大きな進展があった。この機会に、フロントエンドで起きる奇妙で難解な問題の特徴と、対処してきた経験を整理したい。

***

#### 難解な問題は、コードに問題があるかどうかで二つに分けられる

* 一つ目は、コードに非常に見つけにくい設計上の欠陥がある場合だ。潜んでいた論理的な欠陥が、特定の条件で初めて表れる。非同期、並行実行、状態への仮定が適切でなく、低性能の環境や特定の実行順序でだけ露呈する、といったことだ。最も多く遭遇したのは実行順序の競合で、後で例を挙げる。
* 二つ目は、根本原因がコードそのものにはなく、不確実性に満ちたシステムの中で真相を探す場合だ。ここに、フロントエンドの問題調査の難しさがある。正しいコードを書くだけでなく、それがどのような世界で動くかを理解する必要がある。その世界を「不確実なランタイム」と呼べる。不確実性の大きさは、観測も再現も難しくする。

***

#### 不確実なランタイム

一種の技術的な仮定であり、「コード以外はすべて信頼できない」と考える。防御を十分に行うためだ。

コードがbuildされる時点から、不確実なランタイムが始まると考えてよい。

> Build（人による選定）→ Container（強いカスタマイズ）→ OS & Hardware（高度な断片化）→ User & Physical Context（制御不能）。

**1. Build：基盤とビルド成果物の違い**

Polyfill、ランタイムライブラリ、ビルド設定のバージョンが一致しないと、新しい構文が古い環境でエラーになったり、重複した、あるいは誤った注入で例外が起きたりする。

> 少し前に月付で起きた問題では、WebpackからRspackへビルドを切り替えた後、低性能の端末でPromise/thenableの実行順序がおかしくなった。ビルドツール自体にバグがあるとは限らず、成果物のruntimeの振る舞いの違いが問題を起こすこともある。Webpackには、Promise/thenableでモジュールの読み込み順を調整する複雑なruntimeがあるからだ。
>
> 加盟店向け決済画面でも、快驢アプリでset cookieが効かなくなる問題があった。クライアント内の異なるスレッド間の実行順序が原因だった。

**2. Container：ホストアプリとWebViewエンジンの違い**

WebViewごとに、JSの実行、CSSの描画、安全性、インターフェースの振る舞いは完全には一致しない。外側のアプリがAPIを注入したり制限したりするため、同じH5でもコンテナーによって異なる動作になる。

> 典型的なのがiOSのインターセプターだ。コンテナーがネットワークリクエストや画面遷移に介入する仕組みを持ち、実装の誤りや重複が問題を生む。慧銷ではset cookieの失敗によりtokenが無効になった。フードデリバリーの加盟店アプリでは、オフライン化で問題に遭遇した。
>
> 同じ加盟店アプリには、knb.jsがなければ自動で1.9.1を読み込むhookが注入されることもあった。その結果、Androidではappearの購読に成功しても、時折コールバックが実行されなかった。

**3. OS & Hardware：OSと端末の断片化**

メーカーのROM、機種、解像度、OSバージョンの違いによって、特定機種だけのクラッシュ、レイアウトの異常、性能問題が起こりうる。

> フードデリバリー加盟店向けのiOSアプリで、同じ利用者がyodaを大量に繰り返し呼ぶ問題が起きた。翌年、別の問題を解く際に一緒に扱えるとわかり、低性能端末の空きメモリ不足が原因だと突き止めた。メモリ警告後、稼働を維持する仕組みによってWebViewが何度もreloadを呼び、同じ利用者が短時間に繰り返しページへ入っていた。

**4. User & Physical Context：実際の利用環境の違い**

利用者の使い方や環境は千差万別だ。弱い通信、電池残量の低下、保存領域の不足など、制御できない変数が多く、端の条件で異常を生む。再現性が低いため調査は難しく、計測ログと操作履歴を組み合わせて手がかりを探す必要がある。

> 外部決済では、WeChatの複製起動によって決済が失敗したことがある。OSや外部ツールの機能で、同じアプリを一台に二つ起動する利用者がいる。この仕組みはアプリが単一であるという仮定やIPCの経路を壊し、予想外の問題を起こす。

***

#### 多くの失敗から得たことと、二つの事例

一、自分のこだわりを捨てる。すべての難問を、直ちに「完全に説明」する必要はない。すでに事故になっているなら、最優先は周囲からの圧力を和らげることだ。制御できる回避策でリスクを封じ、皆の関心が早くほかへ移れるようにする。大半の人は「究極の根本原因」にこだわらない。まず状況を安定させて初めて、時間をかけて真相を追える。

二、粘り強く挑戦に向き合ってこそ、技術の深さが身につく。長く待った末に、突然解ける問題も多い。行き詰まったら、いったん置いて機会やひらめきを待ち、足りないものを補えばよい。ただし、諦めない。

***

#### 事例一：Webpackのビルド成果物が安定しない問題

冒頭のWebpackの問題は、3年近くかけて浅いところから深いところへ、真相に近づいていった過程だった。

1. 症状。Webpackの成果物がときどき使えなくなり、本番で白画面がまれに発生した。最初は根本原因が見つからなかったが、その過程でパイプラインの配布機構を改善し、本番の許可リストで検証したものと全体公開するものが同じパッケージになるようにした。
2. 設定の分析。その後、洪泽がWebpackの設定を詳しく調べ、外部依存のESMとCJSの混在がビルド異常を起こすと特定した。設定を改善したが、不正な成果物はなお生じた。
3. チームでの実験と妥協。昨年、剑钻が基礎技術の指導でビルド異常を取り上げ、皆の前で成果物が安定しない問題を再現した。この実験でチームの理解が深まった。同時に、より有効な設定も見つかった。非同期のバンドル・ビルドを無効にすれば、不正な成果物は出なくなった。代償はビルド速度だった。
4. 最終的な解決。先週、剑钻が新しい策を示した。成果物にあるn.n()のラッパーという重要な特徴を見つけ、CJS圧縮後の特徴が期待どおりか検査することで、本番パッケージの正しさを保証する。品質リスクが漏れる余地を完全に塞げる。

ここまで来れば、この問題は終えてよいと思う。さらに余力があれば、WebpackのCJSモジュールの非同期ビルド問題を根本から直し、PRを送ればよい。

***

#### 事例二：Alphaプロジェクトのマーケティング機能の再設計

問題は解決できても、すべてに完全な技術的説明が付くわけではない。対処の過程のほうが重要だ。

Alphaでは、あるマーケティング機能の再設計によって口座開設の転換率が下がった。解決まで2か月以上かかり、一連の問題が露呈した。最後まで技術的な根本原因は見つからず、再現する条件も失われた。完全な技術的説明は、永遠に得られない。

それでも、この過程で品質への理解は深まった。業務指標をリアルタイムで観測する能力、AB実験、開発・テスト環境での品質管理の日常化を整え、ロールバックの戦略と判断を深く見直した。この出来事は翌年のAlpha全体の再設計にも直接つながり、チームにとって保守性のよい基準となった。
