迷宮入りのバグと、言葉の壁。「5回のなぜ」だけでは戦えない、海外開発現場のリアル
はじめに:異国の地で「バグ」と向き合うということ
みなさん、こんにちは。海外でC#とWPF(Windows Presentation Foundation)を武器に、日々コードと格闘しているエンジニアです。
突然ですが、皆さんは海外のエンジニアチームで働いたことはありますか?
日本で働いていた頃、僕は「技術があれば言葉なんて関係ない、コードが共通言語だ」なんて思っていました。でも、実際にこっちに来て、多国籍なチームの中で大規模なアプリケーションの設計・開発に携わってみると、その考えが半分は正解で、半分は甘い幻想だったことに気づかされます。
特に、**「深刻なバグ」**が発生した時。その時こそ、エンジニアとしての真価、そして「コミュニケーションの質」が残酷なほど試される瞬間はありません。
今日は、僕が海外の現場で幾度となく冷や汗をかき、そして救われてきた一つの強力な思考ツールについて話をしたいと思います。それは、最新のデバッグツールでも、AIによるコード解析でもありません。もっとアナログで、しかし驚くほど本質的なアプローチ。**「石川ダイアグラム(特性要因図、別名:フィッシュボーン・ダイアグラム)」**です。
「え、それって製造業の品質管理(QC)の話でしょ? ソフトウェア開発に関係あるの?」
そう思ったあなたにこそ、読んでほしい。特に、将来海外で働きたい、あるいは今まさに多国籍チームでの議論に疲弊しているあなたにとって、これは単なるバグ解析手法ではなく、チームを動かす「政治力」すら生み出す最強の武器になるからです。
「5回のなぜ(5 Whys)」の限界と、海外特有の落とし穴
エンジニアなら誰でも、「なぜなぜ分析(5 Whys)」については知っていますよね。トヨタ生産方式に由来するこの手法は、問題の根本原因(Root Cause)にたどり着くための王道です。
- なぜアプリがクラッシュした? → メモリ不足だから。
- なぜメモリ不足になった? → 画像処理のループで解放漏れがあるから。
- なぜ解放漏れが起きた? →
IDisposableの実装が漏れていたから。 - なぜ実装が漏れた? → コードレビューのチェックリストになかったから。
- なぜリストになかった? → 非同期処理のエラーハンドリングに関する知見がチームになかったから。
素晴らしいですよね。論理的で、一直線に深掘りできる。僕も日本にいた頃はこれで戦えていました。
しかし、海外の現場、特に複雑怪奇なレガシーコードや、複数のマイクロサービス、そしてWPFのようなリッチなUIスレッドとバックグラウンド処理が絡み合うシステムにおいて、この「一直線の掘り下げ」はしばしば機能不全に陥ります。
理由は2つあります。
1. 現代のソフトウェアバグは「一直線」ではない
僕たちが扱うC# WPFの現場では、例えば「UIが特定の操作でフリーズする」というバグがあったとします。これ、原因が一つであることなんて稀なんです。
「データベースのレスポンス遅延」と「UIスレッドでの重い処理」と「ユーザーの特定操作のタイミング」という複数の要因が重なり合った時だけ発生する、いわゆる「ハイゼンバグ(再現性の低いバグ)」がほとんど。
これを「なぜ?」と一直線に掘り下げようとすると、途中で「DBが遅いからだ」と決めつけたり、「UIの実装が悪い」と誰かを責めたりして、**他の重要な要因を見落とす(トンネル・ビジョン)**ことになります。
2. 「Why?」は攻撃に聞こえる
これが海外特有の、そして最も厄介な点です。
多国籍チームには、ハイコンテクストな文化(日本やアジア系)と、ローコンテクストな文化(欧米系)が混在しています。英語が母国語でない僕たちが、会議で深刻な顔をして「Why did this happen?」「Why?」「Why?」と連呼するとどうなるか。
相手は詰められていると感じます。「お前のコードが悪いと言いたいのか?」と、防御的な姿勢(Defensive)を取らせてしまうのです。一度こうなると、建設的な議論は不可能です。心理的安全性が崩壊した現場で、真のバグ原因なんて見つかりません。
僕自身、渡航したての頃、この失敗を犯しました。複雑な非同期処理のバグに対して「Why」を繰り返し、同僚のフランス人エンジニアと大喧嘩になりかけたことがあります。彼は「俺のロジックは正しい!」と主張し、僕は「でも動いてないじゃん!」と返す。不毛でした。
そこで出会ったのが、問題を「線」ではなく「面」で捉え、かつ視覚的に共有できる「石川ダイアグラム」のアプローチだったのです。
視覚化という「世界共通言語」
Shutterstock
石川ダイアグラムは、その名の通り、魚の骨のような形をしています。
右端に「問題(結果)」という魚の頭を置き、そこに向かって背骨を引きます。そして、その背骨に対して「大骨(要因のカテゴリ)」を生やし、さらに「小骨(具体的な原因)」を書き込んでいく。
これをソフトウェア開発、特に複雑なバグ解析に導入すると、魔法のような効果が生まれます。
「5 Whys」の完璧な相棒(Visual Companion)
誤解しないでほしいのは、「5 Whys」がダメなわけではありません。石川ダイアグラムは、5 Whysを捨てるものではなく、5 Whysを「並列処理」するための地図なんです。
一つの骨(要因)に対して「なぜ?」を問いながら、同時に別の骨(別の可能性)も視界に入れておく。これにより、バグを「誰かのミス」という点ではなく、「システムの構造的な欠陥」という全体像として捉えることができます。
この図をホワイトボードに書き出した瞬間、チームの空気が変わります。
みんなが「人」ではなく、「ボード上の図」を見て話し始めるのです。
「ここ、Networkの問題だと思ってたけど、実はUIスレッドのブロッキングと相関してないか?」
「Environment(環境)の要因、OSのバージョン違いもこの骨に追加すべきじゃないか?」
僕の拙い英語でも、矢印を引き、骨を書き足すだけで、意図が100%伝わる。
「Why?(なんでやったの?)」という追及が、「Look at this branch(この要因を見てくれ)」という提案に変わる。
この瞬間、僕は海外で働くエンジニアとして、一つの大きな武器を手に入れたと確信しました。
今回のケーススタディ:WPFアプリケーションの「謎のフリーズ」
さて、概念だけ話していても退屈ですよね。エンジニアならコードと事象で語りましょう。
今回、僕たちが一緒に解剖していくのは、実際に僕の現場で起きた、とあるWPFアプリケーションの悪夢のようなバグです。
- アプリケーション: 証券トレーダー向けのリアルタイム・データ・モニタリング・ツール。
- 技術スタック: C# (.NET 6), WPF (MVVM Prism), gRPC for backend communication.
- 事象: 「特定の銘柄が急騰したタイミングで、トレーダーが注文画面を開くと、アプリケーション全体が3〜5秒間フリーズする(Not Respondingになる)。ただし、開発環境では再現しない。」
これ、WPFエンジニアなら聞いただけで胃が痛くなるやつですよね?
UIスレッドがブロックされているのは明白ですが、原因はどこにあるのか。
大量のデータ着信? コンバーターの負荷? メモリリーク? それともサードパーティ製のチャートコントロールのバグ? ネットワークの遅延?
初心者の頃の僕なら、ここで闇雲にプロファイラーを回したり、怪しい箇所にDebug.WriteLineを仕込んだりしていました。でも、それは「木を見て森を見ず」。複雑な相互作用を見落とす典型的なパターンです。
ここで僕は、チームメンバー(バックエンド担当のインド人、UI担当のドイツ人、QAのアメリカ人)を会議室に集め、ホワイトボードのキャップを外しました。
「みんな、ログを追う前に、一度頭の中を全部このボードに出してみよう。Fishboneを描くぞ」
ここからが、本当のエンジニアリングの始まりです。
次回「承」のパートでは、この複雑な事象をどのようにカテゴリ分けし、石川ダイアグラムに落とし込んでいったのか。その具体的な構築プロセスと、ソフトウェア開発向けにカスタマイズした「4M(Man, Machine, Material, Method)」の変則的な使い方について、実況中継のように解説していきます。
これを読み終える頃には、あなたもきっと、次のバグに遭遇するのが少し楽しみになっているはずです。なぜなら、バグはもう「恐怖の対象」ではなく、「解剖すべき興味深い標本」に見えてくるからです。
ホワイトボードを武器にせよ。WPFの「フリーズ」を解剖するフィッシュボーン構築ライブ
戦場は会議室、武器はマーカーペン
会議室の空気は重いままです。
ドイツ人のUI担当、クラウス(仮名)は腕組みをして不機嫌そうに窓の外を見ています。「俺のViewModelのロジックは完璧だ。バックエンドから変なデータが来てるに決まってる」という顔です。
一方、バックエンド担当のラジェッシュ(仮名)は、PCのログ画面を指差しながら「gRPCのストリームは正常だ!レイテンシも許容範囲内だ!」と早口でまくし立てようとしています。
これが、バグ対応の最悪の初期段階。「犯人探し(Blame Game)」の始まりです。
ここで僕たち日本人がやりがちなのは、「まあまあ」と仲裁に入ることですが、海外ではそれは機能しません。必要なのは「事実」と「構造」だけです。
僕はホワイトボードの真ん中に一本、長い直線を引きました。
そして右端に大きく、四角で囲った文字を書きます。
【PROBLEM: Trading App Freezes (3-5s) on Order Screen Entry during High Volatility】
(問題:高ボラティリティ時に注文画面を開くと、アプリが3〜5秒フリーズする)
「OK、みんな。これが我々が倒すべきモンスターだ。誰が悪いかじゃない。システムの『何』がこれを引き起こしているか、可能性を全部洗い出すぞ」
僕は振り返り、魚の背骨に向かって、大きく4本の「大骨(カテゴリ)」を生やしました。
製造業ならここで「4M(Man, Machine, Material, Method)」を使いますが、ソフトウェアエンジニアリング、特にモダンなアプリ開発では、以下の4つ(あるいは6つ)にカスタマイズするのが鉄則です。
- Environment (環境):OS、ネットワーク、ハードウェア、ライブラリ。
- Data / Input (データ):入力値、データ量、タイミング。
- Logic / Code (実装):アルゴリズム、スレッド管理、メモリ。
- Human / Process (操作・手順):ユーザーの操作フロー、設定ミス。
「さあ、埋めていこう。思いつくことは全部だ。正解かどうかなんて気にしなくていい」
カテゴリ1:Environment(環境)—「なぜ俺のPCでは動くんだ?」
まずは一番排除しやすいところから攻めます。
「クラウス、君の開発機では再現しないと言ったよね? 本番環境(Production)と何が違う?」
クラウスが口を開きます。「画面解像度だ。トレーダーは4Kモニターを4枚使ってる。俺たちはフルHDだ」
僕はすかさず「Display / Resolution」という小骨を書き込みます。WPFにおいて、描画領域の広さはレンダリング負荷に直結しますからね。
次にQA担当が手を挙げます。「彼らはVDI(仮想デスクトップ)を使ってるわ。GPUアクセラレーションが効いてない可能性がある」
「Nice catch!」
僕は「Hardware」の枝に「VDI / No GPU」と書き足します。WPFはDirectXベースです。GPU支援がない環境での複雑な描画は、CPUを食いつぶし、UIスレッドを窒息させる要因になり得ます。
- Network: 社内LAN vs VPN?
- Anti-virus: スキャンがファイルアクセスをブロックしてないか?
こうして「環境」の骨が埋まってくると、不思議とチームの敵対心が薄れてきます。「環境のせいかもしれない」という可能性は、エンジニアのプライドを守る安全弁になるからです。
カテゴリ2:Data / Input(データ)—「量の暴力」
次に、今回の一番怪しい要因である「高ボラティリティ(相場変動)」に切り込みます。
ここでラジェッシュの出番です。
「ラジェッシュ、急騰時のデータ量は通常時の何倍だ?」
「少なくとも10倍だ。秒間500ティック以上の更新が来ることもある」
「OK。それはどのスレッドで処理されてる?」
「バックグラウンドのワーカースレッドだよ。だからUIはブロックしないはずだ!」彼はまだ防御的です。
僕は「Data Volume」という枝を描き、そこに「High Frequency Updates (>500/sec)」と書き込みました。
そして、あえて意地悪な質問を投げかけます。
「そのデータ、全部UIに流してる?」
「…フィルタリングはしてるけど、基本は全部ViewModelに渡してる」
ここで僕は、フィッシュボーンの重要なテクニックを使います。**「要因の相関線」**を意識することです。
データ量の骨の近くに、「Data Concurrency(同時実行性)」という枝を追加しました。
「大量のデータが来る。それはいい。でも、それが『注文画面を開いた瞬間』と重なるのが問題なんだ」
カテゴリ3:Logic / Code(実装)— WPFの闇を暴く
ここがエンジニアとして一番腕の鳴るパートです。C# WPF特有の落とし穴を洗い出していきます。
フィッシュボーンの「Logic」エリアは、最も骨が密集する場所になります。
僕はマーカーの色を変え(赤)、WPFの急所を書き込んでいきました。
a) The UI Thread Bottleneck (UIスレッドのボトルネック)
「注文画面を開く(Initialize)」処理と、「価格更新(Update)」処理。この2つがUIスレッド(Dispatcher)を取り合っていないか?
- Construct: 画面構築コスト。
- Binding: データバインディングの解決コスト。
b) Visual Tree Complexity (ビジュアルツリーの複雑さ)
「注文画面、コントロール多すぎないか?」
「DataGridの中にチャートを入れてるだろう? しかもリアルタイム更新の」
クラウスが頷く。「ああ。行ごとにミニチャートを出してる。あれが重いのは知ってるが、ビジネス側の要求だ」
僕は「Heavy Rendering」という小骨を書き、そこに「Virtualization (仮想化) OFF?」と疑問符を付けました。WPFのDataGridで仮想化が切れていたら、大量データ時に死にます。
c) Memory / GC (ガベージコレクション)
「秒間500回の更新で、毎回新しいオブジェクトを作ってないか?」
C#において、短命なオブジェクトの大量生成はGC(ガベージコレクション)を頻発させます。「Stop the World」が起きれば、アプリはフリーズしたように見えます。
「Allocation Rate (ヒープ割り当て率)」という小骨を追加。
カテゴリ4:Human / Interaction(操作)— ユーザーの予期せぬ行動
最後に、「ユーザー」の要因です。
「彼らは注文画面を開く時、ただ開くだけか? それとも別の操作もしてる?」
QA担当が言います。「ログを見ると、彼らは急騰した瞬間に、複数の銘柄の注文画面を『連打』で開こうとしてるわ」
「連打?」
「ええ。マウスでカチカチカチッと」
これは大きな発見です。WPFのウィンドウ生成は重い処理です。それを連打?
「Rapid Trigger」という骨を追加。
「点」が「線」に繋がる瞬間
さて、ホワイトボードはもう魚の骨というより、無数の矢印が飛び交うジャングルの地図のようになっています。
しかし、ここまで書き出したことで、チーム全員がある一つの**「最悪のシナリオ(Perfect Storm)」**に気づき始めました。
僕はボードから一歩下がり、全体を眺めて言いました。
「見てくれ。単体のバグじゃない。これは『複合事故』だ」
僕は赤ペンで、離れた場所にある3つの要因をぐるりと丸で囲み、それらを線で繋ぎました。
- Data: 秒間500回のデータ着信(バックグラウンド処理)。
- Logic: それを
ObservableCollectionに追加し、BindingOperations.EnableCollectionSynchronizationでUIスレッドにマーシャリングしている(ロックが発生)。 - UI: そこへ、トレーダーが注文画面を開く(重いVisual Treeの構築)。
- Environment: GPUの効かないVDI環境での描画。
「解説しよう。バックエンドからのデータ洪水が、ロック(Lock)をかけてコレクションを守ろうとしている。そこへ、UIスレッドが新しいウィンドウの描画のために大量のリソースを要求する。
本来ならUIスレッドは描画を優先したいが、データ更新のロック待ち(Lock Contention)が発生している。さらにVDI環境で描画そのものが遅延し、GCが追い討ちをかける…
結果、UIスレッドは3秒間、呼吸ができなくなっているんだ」
ラジェッシュが「Oh…」と声を漏らしました。
クラウスも腕組みを解き、「つまり、僕のコードが悪いわけでも、君のデータが悪いわけでもない。その『繋ぎ目』が腐ってるってことか」と言いました。
「その通り(Exactly)。」
石川ダイアグラムがもたらした「心理的安全性」
この瞬間、会議室の空気は「誰のせいか?」という殺伐としたものから、「どうやってこのパズルを解くか?」というクリエイティブなものへと一変しました。
フィッシュボーン・ダイアグラムの真価はここにあります。
頭の中にある漠然とした不安や疑念を、すべて「書き出す」ことで、問題の外在化(Externalization)ができるのです。
「ラジェッシュが悪い」のではなく、「データフローの設計」というボード上の骨が悪い。そう認識できたとき、エンジニアは初めてチームとして機能します。
しかし、これで終わりではありません。
図は描けました。仮説も立ちました。でも、これはまだ「推測」の域を出ません。
ここからがエンジニアリングの真骨頂。この仮説を「証明」し、修正し、そして二度と同じ過ちを繰り返さないための「仕組み」を作る必要があります。
次回の「転」では、このフィッシュボーンで見えた仮説を元に、どのようにして検証を行い、意外な真犯人(実は先ほど挙げた複合要因の中に、もう一つ隠れたスパイスがあったのです)をあぶり出したのか。
そして、この図がどのようにして「再発防止策」へと進化していくのかをお話しします。
実は、この「隠れた真犯人」を見つけたのは、一番静かだったあのメンバーの一言でした。
見えない線が見えてくる。相関関係をあぶり出し、チームの「共通言語」を作る瞬間
仮説の検証:プロファイラーは嘘をつかない
ホワイトボードに描かれた「巨大な魚の骨」。
僕たちはその中から、最も可能性が高いと踏んだ「複合要因(Deadly Combination)」にターゲットを絞りました。
仮説:
「バックグラウンドからの大量データ流入(Data)」が、「コレクションのロック(Logic)」を引き起こし、そこに「VDI環境下の重い描画負荷(Environment)」が重なることで、UIスレッドが窒息(Starvation)している。
この仮説が正しいかどうか、議論で終わらせてはいけません。ここからは外科手術の時間です。僕は開発用マシンではなく、問題が再現しているQA環境のVDIにリモート接続し、Microsoft純正の最強プロファイリングツール**「PerfView」と、Visual Studioの「Diagnostic Tools」**を起動しました。
「よし、再現させるぞ。ラジェッシュ、負荷ツールで擬似データを流し込んでくれ。クラウス、注文画面を連打だ」
「Go!」
画面上のグラフが跳ね上がります。CPU使用率が100%に張り付き、Garbage Collection (GC) の黄色いマーカーが頻繁に点滅します。そして、UIは見事にフリーズ。「応答なし」の白い膜がウィンドウを覆いました。
「Gotcha(捕らえた)…」
データを解析すると、予想通りでした。
UI Thread Delayの項目を見ると、System.Windows.Threading.Dispatcherが、Monitor.Enter(ロック待ち)で長時間ブロックされています。
ViewModelが持つObservableCollectionに対し、バックグラウンドスレッドが書き込みを行い、UIスレッドが読み込みを行う。ここで競合が発生し、描画処理が後回しにされている。
「ビンゴだ! 図の通りだぞ!」
クラウスが興奮して叫びました。「やっぱりロックの競合だ! BindingOperations.EnableCollectionSynchronizationを使っているとはいえ、更新頻度が高すぎるんだ!」
修正、そして…消えない違和感
原因は特定できました。解決策も明白です。
僕たちはすぐさま修正パッチを作成しました。
- Throttling(間引き)の導入: 秒間500回の更新をすべてUIに流すのをやめ、バッファリングして「100ミリ秒に1回」まとめて更新するように変更(Reactive Extensionsの
Bufferを使用)。 - Virtualization(仮想化)の強制: DataGridの行の仮想化が確実に効くようにXAMLを修正。
「これで完璧なはずだ。論理的に、UIスレッドの負荷は10分の1以下になる」
修正版をデプロイし、再度テストを行いました。
チーム全員が固唾をのんでモニターを見守ります。注文画面が開く。データが流れる。
…画面は、動きました。
フリーズは消えました。
「やったか!」
ラジェッシュがハイタッチを求めようとした、その時です。
「Wait(待って)。」
部屋の隅で、一番若手のジュニアエンジニア、ベン(Ben)が声を上げました。彼はいつもヘッドホンをして黙々と作業するタイプですが、この日はテストの様子をじっと見ていました。
「…まだ、カクついてない?(Is it still stuttering?)」
言われてみれば、確かにフリーズ(完全停止)はしていませんが、スクロールが微妙に引っかかります。いわゆる「Jank(カクつき)」です。
「いや、許容範囲だろう?」とクラウスは言いましたが、僕は背筋に冷たいものを感じました。
スロットリングを入れたんです。データ量は激減しているはず。なのに、なぜまだCPUが唸っているんだ?
もう一度PerfViewを見ました。
確かにロック待ちは消えています。しかし、**「UIスレッドの占有時間」**自体は、なぜか高いままなのです。
何かが、まだそこにいる。
フィッシュボーン・ダイアグラムの「骨」のどれか、あるいは、まだ描かれていない「見えない骨」が、悪さをしている。
静寂を破る「意外な一言」
会議室に沈黙が戻りました。
ホワイトボードの図を見上げます。
Environment? Data? Logic? Human?
全部チェックしたはずだ。
その時、ベンがボソッと言いました。
「ねえ、なんでOutputウィンドウの文字が、そんなに速く流れてるの?」
え?
僕はVisual Studioの「出力(Output)」ウィンドウを見ました。
そこには、猛烈な勢いでテキストが流れていました。早すぎて読めないほどです。
「これ…何だ?」
スクロールを止めて、ログを確認します。
そこには、延々とこう書かれていました。
Plaintext
[DEBUG] OrderUpdate Received: IsBuy=True, Price=150.23, Symbol=MSFT, Timestamp=... {massive JSON object}...
[DEBUG] OrderUpdate Received: IsBuy=False, Price=150.25, Symbol=MSFT, Timestamp=... {massive JSON object}...
「誰だ、このログ仕込んだのは!」
ラジェッシュが「俺じゃないぞ!」と手を振ります。
クラウスが顔を青くして言いました。「…あ。それ、俺かも」
「なに?」
「いや、先週のデバッグ中に、データの流れが追えなくて… ToString()したオブジェクトの中身を全部Trace.WriteLineに出すようにしたんだ。でも、#if DEBUGで囲ったはずだぞ?」
僕はコードを確認しました。
確かにTrace.WriteLineがありました。しかし、それは#if DEBUGの外、つまりリリースビルドでも動作する場所に残っていたのです。
さらに最悪なことに、ログに出力しようとしていたオブジェクトは、複雑な階層構造を持つ巨大なデータクラスでした。これをToString()(シリアライズ)するコスト。そして、それをコンソール(標準出力)に書き出すコスト。
「Wait, wait, wait…」
僕はホワイトボードに駆け寄り、赤いマーカーを握りしめました。
「みんな、フィッシュボーンを見てくれ。ここには『Logic』がある。ここには『Data Volume』がある。
でも、我々が見落としていた骨がある。それはここだ!」
僕は「Logic」と「Environment」の間に、新しい大きな骨を一本、乱暴に描き足しました。
【Diagnostic / Logging (診断・ログ)】
「Trace出力は、デフォルトではリスナーが設定されていなければ無害に近い。でも、Visual Studioでデバッグ実行している時、あるいは特定の監視ツールが入っている時、これは同期的に処理されることがある。
しかも、UIスレッドが忙しい時に、大量の文字列連結とI/Oを強いているんだ。
秒間500回、巨大なJSONをシリアライズしてコンソールに吐き出す?
それは、毎秒辞書一冊分を書き写しながらマラソンするようなもんだぞ!」
「隠れた真犯人」との対峙
ベンの一言が、すべての点と点を繋ぎました。
- ThrottlingでUI更新は減った。 だから「完全フリーズ」は直った。
- しかし、バックグラウンドでのデータ受信自体は秒間500回続いている。
- その受信処理の中で、**「全件ログ出力」**が行われていた。
- このログ出力がCPUリソースを食いつぶし、間接的にUIスレッドのパフォーマンスを落としていた(Jankの原因)。
これは、単なる「消し忘れ」ではありません。
「高負荷時の挙動」を予測できなかった、我々の想像力の欠如です。
フィッシュボーン図において、僕たちは「機能(Function)」ばかりに目を向けていました。「デバッグ機能(Non-functional requirement)」が、本番環境の足を引っ張る可能性を、図に描いていなかったのです。
「修正しよう。一行削除だ」
震える手でコードを削除し、再ビルド。
そして、実行。
…ヌルヌルです。
絹のように滑らかなスクロール。
CPU使用率は5%以下。
先ほどまでのカクつきが嘘のように消え去りました。
「Wow…」
ベンが小さく呟き、ラジェッシュが口笛を吹きました。
クラウスは「ごめん」と小さく言い、その後すぐに「でも、ベン、よく気づいたな!」と若手の肩を叩きました。
図が「生きたドキュメント」に変わる
トラブルは解決しました。しかし、僕の仕事はまだ終わりではありません。
僕はホワイトボードに向き直り、チーム全員に言いました。
「OK、これで解決だ。でも、消す前にもう一つ仕事がある」
僕は、先ほど描き足した「Diagnostic / Logging」の骨を、正式な「要因」として清書しました。
そして、その横に青いペンでこう書き添えました。
- Lesson Learned: “Logging heavy objects in hot path kills performance.” (ホットパスでの巨大オブジェクトのログ出力はパフォーマンスを殺す)
- Action Item: “Add Lint rule to forbid Trace in loops.” (ループ内でのTraceを禁止するLintルールを追加する)
「この図は、今回のバグの『墓標』じゃない。僕たちのチームの『資産(Asset)』だ。
次、また別のパフォーマンス問題が起きた時、僕たちは真っ先にこの図を思い出すだろう。
『おい、また誰かログを出しっぱなしにしてないか?』ってね」
フィッシュボーン・ダイアグラムは、最初はただの「仮説の地図」でした。
しかし、議論し、検証し、失敗し、そして新たな発見を書き加えることで、それは**「チームの経験値そのもの」**に進化したのです。
もし、最初に「Why?(なぜログが出てる?)」とだけ追求していたら、クラウスは「ミスった」と萎縮して終わっていたでしょう。
しかし、図があったおかげで、これは「個人のミス」ではなく、「Loggingというシステムの落とし穴」として構造的に理解されました。
だからこそ、ベンも発言しやすかったし、クラウスも素直に認め、チーム全体での再発防止策(Lintルールの導入)へと話が進んだのです。
これが、「転」の物語です。
技術的な知識(ロック、スレッド、ログ)はもちろん重要です。しかし、それ以上に重要なのは、**「想定外の事態が起きた時、何に立ち返るか」**という拠り所です。
石川ダイアグラムは、迷走しがちなトラブルシューティングにおいて、常に「我々が見ている全体像」を提示し続けてくれました。
さて、長い戦いも終わり、バグは消滅しました。
しかし、この経験は僕たちに、単なるバグ修正以上のものをもたらしました。
最終回となる「結」では、この一件を通じてチームがどのように変化したか、そして「エンジニアの価値」とは結局どこにあるのかについて、締めくくりたいと思います。
エンジニアの価値は「解決」の前に「可視化」にある。品質管理ツールが教えてくれたサバイバル術
祭りのあと、静寂の中で残ったもの
WPFアプリケーションの「3秒間のフリーズ」が解消された翌日。
オフィスの雰囲気は、まるで嵐が過ぎ去った後のように穏やかでした。
あの悪夢のようなログ出力コード(Trace.WriteLine)は削除され、代わりに適切なロギング戦略とLintルールが導入されました。アプリは高負荷時でも、まるで氷の上を滑るようにスムーズに動作しています。
しかし、僕にとって最大の成果は、修正されたC#のコードではありませんでした。
それは、ホワイトボードに残された、あの乱雑で、修正液の跡だらけの「フィッシュボーン図」でした。
いつもなら、解決したらすぐに消してしまうホワイトボード。
でも今回は、ドイツ人のクラウスが「記念に残しておこうぜ」と言って、スマホで写真を撮っていました。
無口なジュニアのベンは、自信ありげに新しいタスクに取り組んでいます。
インド人のラジェッシュは、僕のデスクに来てこう言いました。
「次のスプリント計画でも、あの図を使わないか? 依存関係がクリアになって良かった」
その時、僕はハッと気づきました。
僕たちが作ったのは、バグ修正のための図解ではありませんでした。
僕たちは、**「信頼(Trust)」**を作ったのです。
サバイバル術1:可視化は「文化の翻訳機」である
海外で働きたいと思っているエンジニアの皆さんへ。
技術力はもちろん大切です。C#の非同期処理やメモリ管理に詳しいことは、入場チケットのようなものです。
しかし、そこから一歩抜きん出て、チームの中心(Core)として認められるために必要なもの。
それは、**「カオスに秩序を与える力」**です。
海外の現場は、日本以上に多様で、カオスです。
- 文化背景が違う(ハイコンテクスト vs ローコンテクスト)。
- 母国語が違う(英語ネイティブ vs 非ネイティブ)。
- 技術スタックへの思想が違う。
この状況で、言葉だけで「論理」を戦わせようとすると、どうしても「声の大きい人」や「英語が流暢な人」が勝ちます。日本人の私たちは、ここで負けがちです。
しかし、「図(Diagram)」は違います。
フィッシュボーンのような図解ツールは、これらすべての壁を越える**「文化の翻訳機」**として機能します。
言葉で「I think it’s a resource contention issue(リソース競合だと思う)」と言うと、相手は「証拠は?」と身構えます。
でも、図を描きながら「Data」と「UI」の線を結ぶと、相手は「I see(なるほど)」と、同じ方向を見てくれます。
僕がこのトラブルシューティングで学んだ最大の教訓は、**「優れたエンジニアとは、正解を即答できる人ではなく、全員が正解にたどり着ける地図を描ける人である」**ということです。
このスキルは、言葉の壁がある我々日本人エンジニアにとって、最強の生存戦略(Survival Strategy)になります。
サバイバル術2:「心理的安全性」を設計するのもエンジニアの仕事
今回の件で、もし僕がフィッシュボーンを使わず、「5 Whys(なぜなぜ分析)」だけで攻めていたらどうなっていたでしょうか。
「なぜフリーズする?」→「UIスレッドが止まるから」
「なぜ止まる?」→「誰かが重い処理を入れたから」
「誰だ?」→「クラウスか?」
「いや、ログを見るとラジェッシュのデータがおかしい」
「いや、そもそもベンがテストで見逃したんじゃないか?」
こうなっていた確率は100%です。これが「Blame Culture(非難の文化)」です。
この空気の中では、ベンは絶対に「ログが出すぎている」なんて言い出せなかったでしょう。「余計なことを言って、自分のせいにされたらどうしよう」と萎縮するからです。
フィッシュボーン・ダイアグラムは、問題を「人(Who)」から切り離し、「事象(What)」としてホワイトボードに貼り付けました。
「ラジェッシュ vs クラウス」の構図を、「チーム vs 複雑なシステム」の構図に変えたのです。
これが、Googleなどが提唱する**「心理的安全性(Psychological Safety)」**の正体です。
心理的安全性とは、みんなで仲良くすることではありません。「懸念を言っても罰せられない」という確信のことです。
「ここ、おかしくないですか?」とジュニアが言える環境。
それを設計し、実装するのも、シニアエンジニアの重要な「アーキテクチャ設計」の一部なのだと痛感しました。コードの疎結合だけでなく、人の感情の疎結合もデザインする必要があるのです。
サバイバル術3:泥臭いツールこそが、洗練された解決を導く
私たちはITエンジニアなので、すぐにJira、Confluence、Miroといったデジタルツールや、最新のAI解析を使いたがります。
もちろんそれらも素晴らしい。でも、緊迫したトラブルの現場では、「物理的なホワイトボードとペン」、そして**「古臭いQC手法」**が、驚くほどの即効性を持つことがあります。
石川馨博士が考案したこの図は、元々は工場の品質管理のためのものでした。
しかし、ソフトウェアが複雑化し、見えなくなればなるほど、この「物理的に書き出す」という行為の価値は上がっています。
モニターの中だけで完結させないでください。
立ち上がり、ペンを持ち、体を動かして線を引く。
そのパフォーマンス(Performance)自体が、チームの沈滞した空気を動かし、「リーダーシップ」として認知されます。
「あいつがペンを持つと、議論が整理される」
そう思われたら、あなたの海外でのポジションは安泰です。レイオフの対象になることもまずないでしょう。なぜなら、あなたは単なる「コーダー」ではなく、「ファシリテーター」だからです。
最後に:あなたへのアクションプラン
ここまで読んでいただき、本当にありがとうございました。
最後に、明日から現場で使えるアクションプランを提示して、このシリーズを締めくくりたいと思います。
- まずは一人で描いてみる:いきなり会議でやるのが怖ければ、自分のノートで構いません。今抱えているバグや、設計上の課題をフィッシュボーンで分解してみてください。「Environment」「Data」「Logic」…、見落としていた「骨」が見つかるはずです。
- 「Why」を「What」に変える:同僚に質問する時、「Why did you do this?(なんでこうやったの?)」と聞くのをやめてみましょう。代わりに、「What factors led to this choice?(どんな要因がこの選択に繋がったの?)」と聞いてみてください。それだけで、敵対関係は協力関係に変わります。
- 小さな「発見」を称賛する:チームの誰かが、些細なことでも違和感(今回のベンのような気づき)を口にしたら、全力で拾い上げてください。「Good catch!」「Sharp eye!」。その一言が、次のバグを防ぐ防波堤になります。
日本から遠く離れた海外の空の下。
言葉も文化も違う仲間たちと、一つの画面を覗き込み、頭を抱え、そしてハイタッチをする。
大変なことも多いですが、これほどエキサイティングな仕事はありません。
このブログが、これから海を渡ろうとするあなた、あるいは今まさに荒波の中にいるあなたの、小さな羅針盤になることを願っています。
さあ、ペンを持って。
見えない線を、描きに行きましょう。

コメント