【海外エンジニア直伝】バグ地獄からの脱出!「なぜなぜ分析(5 Whys)」で真犯人を追い詰めろ 〜コードの裏に潜む物語を読む技術〜

  1. 終わらないデバッグの夜と、見えない真犯人
      1. 〜症状を消すことと、病を治すことの決定的違い〜
      2. WPFエンジニアが陥る「Nullチェックの麻薬」
      3. 海外の現場で「Why」が求められる理由
      4. バグは「不運な事故」ではなく「必然の結果」
      5. テクノロジーの進化と、取り残される「思考」
      6. 思考の武器「The 5 Whys」との出会い
  2. 深淵へのダイブ:コードの迷宮で「真犯人」を追い詰める実況中継
      1. 〜「5 Whys」という名の、思考のドリル〜
      2. FILE No.404:消えたデータと、フリーズする画面
      3. The 1st Why:なぜ、例外が発生したのか?
      4. The 2nd Why:なぜ、描画中にバックグラウンド更新が走ったのか?
      5. The 3rd Why:なぜ、前の検索が終わっていないのに、結果を書き込もうとしたのか?
      6. The 4th Why:なぜ、キャンセル処理が実装されていなかったのか?
      7. The 5th Why:なぜ、古い(不適切な)パターンが使い回されているのか?
      8. 真犯人の正体
      9. 海外エンジニアの「Value(価値)」とは
  3. 会議室の決闘:正論というナイフで人を刺さないために
      1. 〜「Why」が「Who」に変わる瞬間、プロジェクトは崩壊する〜
      2. 「Post-Mortem(検死会議)」の重苦しい空気
      3. 「5 Whys」が「尋問」に変わるとき
      4. 作戦変更:Egoless Programming(自我を排したプログラミング)
      5. 文化の壁を超える「共通言語」
      6. 小さな成功(Small Win)を積み上げる
      7. 転の教訓:正しさは「優しさ」で包んで初めて機能する
  4. 「なぜ」の果てにあるもの:エンジニアが「哲学者」になる日
      1. 〜コードを書く手は止めても、思考だけは止めるな〜
      2. エンジニアの「市場価値」を決めるもの
      3. 人生のデバッグにも効く「5 Whys」
      4. 読者へのメッセージ:思考の深さが、あなたの深さになる

終わらないデバッグの夜と、見えない真犯人

〜症状を消すことと、病を治すことの決定的違い〜

やあ、みんな。今日もVisual Studioのダークテーマと睨めっこしてるかな?

それとも、XAMLのBindingエラーがOutputウィンドウに吐き出す黄色い文字の滝に、静かに絶望しているところだろうか。

僕は今、海外(ここがどこかは想像に任せるよ、コーヒーが濃くて、議論がもっと濃い場所だ)で、C#とWPFを武器に、デスクトップアプリケーションの設計開発をしている。

海外で働いてみて痛感することの一つ。それは、「動けばいい」というメンタリティと、「なぜ動くのか(あるいは動かないのか)」を論理的に説明する責任の板挟みだ。特に我々のようなアプリケーション層、しかもUIに近いWPFを扱っていると、バグの出方は千差万別だ。

「ボタンを押しても反応しない」

「特定の画面遷移でメモリリークする」

「客先の環境だけで再現する謎のクラッシュ」

正直に言おう。僕たちは忙しい。スプリントの締め切りは迫っているし、Jiraのチケットは増殖する一方だ。そんな時、目の前のバグに対して、僕たちが無意識にやってしまう「悪魔の所業」がある。

そう、**「とりあえずの修正(Quick Fix)」**だ。

WPFエンジニアが陥る「Nullチェックの麻薬」

具体的な話をしようか。WPFでMVVMパターンを使っていると仮定しよう。

ViewからViewModelのコマンドが叩かれ、Model層からデータを取得してプロパティにセットする。素晴らしい非同期処理の舞踏会だ。

ある日、QAチームからこんなチケットが飛んでくる。

「ユーザー設定画面を開くと、アプリが落ちる」

デバッガーをアタッチしてスタックトレースを見る。原因はすぐにわかる。お馴染みの NullReferenceException だ。深い階層にあるプロパティ、例えば UserSettings.Preferences.Theme.Color にアクセスしようとして、Preferences が null だった、みたいな話だ。

ここで、疲れたエンジニア(過去の僕も含めてね)の脳裏に、悪魔が囁くんだ。

「おい、if (UserSettings.Preferences != null) って一行足しちまえよ。そうすりゃ落ちないだろ? チケットはクローズだ。ビール飲みに行こうぜ」

これが「麻薬」だ。

確かにアプリは落ちなくなる。見かけ上の「症状」は消える。上司への報告も「修正しました、デプロイ可能です」と言える。

でも、これは何も解決していない。

なぜなら、「本来あるべきはずのPreferencesが、なぜnullだったのか?」 という問いを放棄しているからだ。

この「とりあえずnullチェック」の積み重ねが、数ヶ月後、あるいは数年後、誰も触れられないスパゲッティコードを生み出す。あちこちに防御的な if 文が張り巡らされ、データの整合性は失われ、本当のバグの原因はコードの深淵に埋もれていく。これを僕は「レガシーコードへの招待状」と呼んでいる。

海外の現場で「Why」が求められる理由

日本で働いていた頃ももちろん「根本原因の究明」は大事だった。でも、海外に出てきてから、この重要度は桁違いに跳ね上がったと感じている。

なぜか? それは**「コンテキスト(文脈)の共有レベルが低い」**からだ。

日本人のチームなら、「まあ、とりあえず動くようにしときました(察してね、忙しいし)」で通じることもある(良くないけどね)。阿吽の呼吸というか、行間を読む文化があるからだ。

でも、多国籍チームではそれは通用しない。

「なぜnullチェックを入れたんだ?」と聞かれて、「落ちてたから」と答えると、彼らは怪訝な顔をする。「落ちていたのは結果(Symptom)だろう? 私が聞いているのは、なぜその状態が発生したか(Root Cause)だ。その修正は、根本的なデータ不整合を隠蔽してしまうリスクがないか?」

ロジカルで、容赦がない。

彼らにとって、論理的な裏付けのない修正は、修正とみなされないのだ。

「Why」を説明できないエンジニアは、ただの「コーダー」として扱われる。設計者(Architect)としてのリスペクトを得るためには、バグという現象を通じて、システム全体の欠陥を論理的に暴き出す能力が必要不可欠なんだ。

特に英語が母国語でない僕たちにとって、この説明はハードルが高い。だからこそ、強力な「思考のフレームワーク」が必要になる。言葉の壁を超えて、ロジックそのもので相手を納得させるための武器だ。

バグは「不運な事故」ではなく「必然の結果」

ここで、少し視点を変えてみよう。

多くのエンジニアは、バグを「自分たちが書いたコードの不注意によるミス」や「たまたま起きた不運な事故」だと捉えがちだ。

「あー、ごめんごめん、Typoしてたわ」とか、「タイミングの問題だったわ」とか。

でも、プロフェッショナルな視点、特に高品質なソフトウェアを提供し続ける組織の視点では、バグはもっと別の意味を持つ。

バグとは、**「プロセス、設計、あるいは組織構造に潜む欠陥が、コードという形を借りて顕在化したもの」**だ。

WPFのBindingが失敗するのは、単にPathの書き間違いかもしれない。でも、なぜ書き間違えたのか? IntelliSenseが効かないような動的なBindingを多用する設計になっていないか? あるいは、ViewModelの設計が複雑すぎて、誰もデータフローを把握できていないのではないか?

そこまで掘り下げないと、同じようなバグは何度でも起きる。

「モグラ叩き」のゲームを一生続けることになる。海外の高い給料をもらって、一生モグラを叩き続ける人生なんて送りたくないよね? 僕たちはクリエイティブな機能開発に時間を使いたいんだ。

だからこそ、僕たちは「探偵」にならなければならない。

目の前の死体(クラッシュしたアプリ)を見て、すぐに「心不全ですね」と診断書を書くヤブ医者ではなく、なぜ心臓が止まったのか、何を食べたのか、誰と会っていたのか、その背景にある物語を読み解く名探偵に。

テクノロジーの進化と、取り残される「思考」

現代の開発環境はものすごく便利になった。

C#は進化し続けている。Null許容参照型(Nullable Reference Types)が導入され、コンパイラがnullの可能性を警告してくれるようになった。WPFのデバッグツールも、SnoopやVisual Studioのライブビジュアルツリーを使えば、何が起きているか視覚的にわかる。

AIだってある。GitHub Copilotにエラーログを投げれば、一瞬で「ここを直せばいいよ」と教えてくれる時代だ。

でも、気をつけてほしい。

ツールが便利になればなるほど、僕たちは「思考停止」に陥りやすくなる。

AIが提示する「修正案」は、多くの場合、ウェブ上の膨大なデータから見つけ出した「最も確率の高い対症療法」に過ぎないことがある(もちろん、素晴らしい洞察をくれることもあるけれど)。

AIは「あなたのチームの開発プロセス」や「その変数がnullになった歴史的経緯」までは知らない。それを知っているのは、現場にいるあなただけだ。

「AIがこう言ってるから」でコードを修正するのは、自分のエンジニアとしての魂をアルゴリズムに売り渡すようなものだ。

ツールはあくまでツール。真犯人を追い詰めるのは、人間の、あなたの論理的思考力だ。

思考の武器「The 5 Whys」との出会い

僕が海外に来て、シニアエンジニアに徹底的に叩き込まれたのが、今回紹介する**「The 5 Whys(なぜなぜ分析)」**だ。

「なんだ、トヨタのあれか」「製造業の話でしょ?」と思ったそこのあなた。

ブラウザバックするのはまだ早い。

確かに元祖はトヨタ生産方式だ。工場のラインで不良品が出た時に使われる手法だ。

だが、これはソフトウェア開発、特に複雑怪奇なバグハントにおいて、驚くべき威力を発揮する。

そして、これを「正しく」使えているエンジニアは、実は驚くほど少ない。

多くの人は「なぜ?」を2回くらい繰り返して満足してしまう。

「なぜ落ちた? → nullだったから」

「なぜnullだった? → 初期化してなかったから」

「よし、初期化コードを追加だ!」

これでは浅い。あまりにも浅い。これでは地下1階までしか降りていない。真犯人は地下5階の独房に潜んでいるのに。

この「5 Whys」というテクニックは、単なる質問リストではない。

これは、「表面的な事象(Symptom)」の皮を一枚ずつ剥ぎ取り、痛みを伴うかもしれない「真の原因(Root Cause)」を直視するための、精神的な修行とも言える。

それは時に、自分自身の過去の設計ミスを認めることであり、チーム内のコミュニケーション不全を暴くことでもある。だから痛い。でも、その痛みの先にしか、堅牢なシステムと、エンジニアとしての真の成長はない。

このブログの後半(承・転・結)では、この「5 Whys」を、我々ITエンジニアが日常的に遭遇する具体的なバグに適用し、どのように思考を深めていくのかを、実況中継のようにお見せしたいと思う。

机上の空論ではない。泥臭い、現場のリアルなデバッグの話だ。

これを読み終える頃には、あなたは次のバグ報告を見るのが少し楽しみになっているかもしれない。「お、また真犯人を見つけるチャンスが来たな」と。

さあ、デバッガーをアタッチする準備はいいかい?

心の準備をしておいてくれ。これから僕たちは、コードの深淵へと潜っていくことになるから。

深淵へのダイブ:コードの迷宮で「真犯人」を追い詰める実況中継

〜「5 Whys」という名の、思考のドリル〜

さて、マインドセットの話は前回たっぷりしたね。

ここからは、実際に僕が海外のオフィスで——窓の外には見慣れない街並みが広がり、同僚たちはサンドイッチ片手に議論している——行っている「探偵業務」の全貌をお見せしよう。

紹介するのは、トヨタが生んだ最強の思考フレームワーク、**「The 5 Whys(なぜなぜ分析)」**だ。

このテクニックの本質は、シンプルだ。「なぜ?」を5回繰り返すこと。

「なんだ、そんなことか」と侮ってはいけない。これは、子供が親を困らせるための「なんで?なんで?」とはわけが違う。

これは**「ドリル」**だ。

コンクリートのように固まった「思い込み」や「表面的な現象」の層を突き破り、地底深くに眠るマグマ(根本原因)に到達するための、鋭利で強力なドリルなんだ。

多くのエンジニアは、1回か2回の「Why」で掘るのをやめてしまう。

「なぜバグった? → ヌルポだったから → 直そう」

これでは、雑草の葉っぱをちぎったに過ぎない。根っこは残っているから、雨が降れば(条件が揃えば)またすぐに生えてくる。

我々が目指すのは、根こそぎ引き抜くことだ。二度と同じ雑草が生えないように土壌改良まですることだ。

では、実際のWPFアプリケーションの開発現場で起きた「ある事件」を例に、このドリルを回してみよう。


FILE No.404:消えたデータと、フリーズする画面

あれは、リリースを目前に控えた金曜日の午後だった。

Slackの通知音が鳴り、QA(品質保証)エンジニアの「Mike」から、不穏なメッセージが届いた。

Mike: “Hey, we have a blocker. The app crashes randomly when I search for customers. It’s rare, but it happens.”

(おい、ブロッカーだ。顧客検索をするとアプリがランダムにクラッシュする。稀だけど、起きるぞ。)

添付されたログファイルには、WPFエンジニアなら親の顔より見たことのある、あの忌まわしい例外が刻まれていた。

System.InvalidOperationException: Collection was modified; enumeration operation may not execute.

さあ、ここからが勝負だ。

いつもの「とりあえず修正」モードの僕なら、こう考えるだろう。

「あーはいはい、foreach中にコレクション書き換えたんでしょ? コピー作って回すようにすればいいじゃん。 .ToList() 追加して終わり!」

だが、今の僕は違う。海外のシニアエンジニアたちに揉まれ、論理的思考の鬼となっている。

Mikeに「I’m looking into it.(調査するよ)」と返信し、ホワイトボードに向かう。

ここから、**「リアルタイム 5 Whys」**の開始だ。

The 1st Why:なぜ、例外が発生したのか?

まずは事実確認だ。スタックトレースを読む。

例外が発生しているのは、顧客リストを表示している ListView のバインディング更新処理の最中だ。

  • Q1: Why did the application crash with InvalidOperationException?(なぜ、InvalidOperationExceptionでクラッシュしたのか?)
  • A1: Because the Customers collection (ObservableCollection) was modified by a background thread while the UI thread was iterating over it to update the view.(UIスレッドが描画のためにコレクションを走査している最中に、バックグラウンドスレッドがそのコレクションを変更したからだ。)

これはWPFあるあるだ。WPFのUIコンポーネントは、原則としてUIスレッド(Main Thread)からしか触れない。しかし、BindingOperations.EnableCollectionSynchronization を使ったり、あるいは不用意に別スレッドからデータを突っ込んだりすると、競合が起きる。

ここで浅いエンジニアはこうする。「lock をかけよう」あるいは「Dispatcher.Invoke でUIスレッドに戻そう」。

でも待て。まだ地下1階だ。なぜそんな競合が起きた?

The 2nd Why:なぜ、描画中にバックグラウンド更新が走ったのか?

コードを確認する。検索処理は Task.Run で非同期に実行されている。

検索ボタンが押されると、ViewModelの SearchCommand が走り、バックグラウンドでDBからデータを取得。その結果を Customers コレクションに Add している。

  • Q2: Why was the background thread modifying the collection exactly when the UI was reading it?(なぜ、UIが読み取っているまさにそのタイミングで、バックグラウンドスレッドがコレクションを変更したのか?)
  • A2: Because the user clicked the “Search” button multiple times rapidly. The first search was still processing (and binding to UI) when the second search result returned and tried to clear/add items.(ユーザーが「検索」ボタンを連打したからだ。1回目の検索結果を処理(UIバインド)している最中に、2回目の検索結果が返ってきて、リストをクリアしたり追加しようとしたためだ。)

なるほど、レースコンディション(競合状態)だ。

ここで中級エンジニアは思う。「ボタンを連打できないように、IsEnabled プロパティをバインドして無効化しよう」。

これは悪くない手だ。UIのユーザビリティとしても正しい。

だが、それでもまだ「根本原因」ではない。もしネットワークが遅延して、ユーザーが別の画面に行ってから戻ってきたら? あるいは、自動更新機能がついたら?

まだ掘れる。ドリルを回せ。

The 3rd Why:なぜ、前の検索が終わっていないのに、結果を書き込もうとしたのか?

ここが重要な分岐点だ。

非同期処理のデザインパターンの問題に踏み込む。

  • Q3: Why did the second search task try to update the collection, even though the first search was no longer relevant (or conflicting)?(なぜ、2回目の検索タスクは、1回目の検索がもう不要(または競合中)であるにも関わらず、コレクションを更新しようとしたのか?)
  • A3: Because the asynchronous search method does not support Cancellation. The previous task was left running “fire and forget”, and there was no mechanism to stop it from touching the shared resource (the collection).(非同期検索メソッドが**「キャンセル」**をサポートしていなかったからだ。前のタスクは「撃ちっ放し(fire and forget)」にされ、共有リソース(コレクション)に触れるのを止めるメカニズムが存在しなかった。)

ここでようやく、技術的な核心に近づいてきた。

問題は「ボタン連打」というユーザーの行動(Symptom)ではなく、**「古いタスクが生き残り続け、ゾンビのように現在の状態を破壊しに来る」**という設計(Root Cause候補)にある。

解決策として CancellationToken を導入し、新しい検索が始まったら古いトークンをキャンセルする、という案が出る。

技術的にはこれで正解に見える。

だが、僕はここで手を止めない。海外の現場では、ここからさらに「組織」や「プロセス」へ矛先を向けるのが流儀だ。

The 4th Why:なぜ、キャンセル処理が実装されていなかったのか?

このプロジェクトのコードベースを見渡してみる。

他の検索画面はどうなっている? ……驚いたことに、他の画面でもキャンセル処理は実装されていない。たまたまデータ量が少なく、一瞬で終わるからバグが顕在化していなかっただけだ。

  • Q4: Why was the Cancellation logic missing from the SearchCommand implementation?(なぜ、SearchCommandの実装にキャンセルロジックが欠落していたのか?)
  • A4: Because the engineer (maybe me, maybe someone else) copied and pasted the boilerplate code from an old BaseViewModel or a previous project which didn’t consider async concurrency best practices.(エンジニア(僕かもしれないし、他の誰かかも)が、非同期の並行処理のベストプラクティスを考慮していない古い BaseViewModel や過去のプロジェクトから、ボイラープレートコードをコピペしたからだ。)

痛い。耳が痛い。

我々は忙しさにかまけて、「動いているコード」を安易にコピペする。

その「コピー元」がそもそも腐っていた(あるいは時代遅れだった)可能性を疑わずに。

The 5th Why:なぜ、古い(不適切な)パターンが使い回されているのか?

いよいよマグマに到達する。これは個人のミスではない。チームの課題だ。

  • Q5: Why are we reusing outdated patterns without updating them for modern async/await requirements?(なぜ、我々はモダンなasync/awaitの要件に合わせてアップデートせずに、時代遅れのパターンを使い回しているのか?)
  • A5: Because we lack a standardized “Async Command Guideline” and code reviews focus only on logic correctness, not on threading safety or architectural consistency.(標準化された「非同期コマンド・ガイドライン」が存在せず、コードレビューがロジックの正しさだけに集中しており、スレッドセーフティやアーキテクチャの一貫性を見ていないからだ。)

真犯人の正体

見えただろうか?

最初の「アプリが落ちる」という現象の真犯人は、foreach 文でもなければ、ユーザーの連打でもない。

真犯人は、**「非同期処理におけるキャンセル設計の標準化不足」「スレッドセーフティを軽視したレビュー体制」**だ。

ここまでの深掘りを経て、初めて僕は修正に取り掛かる。

修正内容は以下の3段構えになる。

  1. 即時対応(Fix):SearchCommand に CancellationTokenSource を導入し、再実行時に古いタスクをキャンセルする。
  2. 恒久対応(Corrective Action):AsyncCommand という基底クラスを作成し、キャンセル処理をカプセル化して、誰でも簡単にキャンセル付きコマンドを書けるようにする。
  3. 予防措置(Preventive Action): チームのWikiに「非同期処理ガイドライン」を追加し、次回のミーティングで「ゾンビタスクの危険性」について5分間のライトニングトークを行う。

海外エンジニアの「Value(価値)」とは

もし僕が「The 1st Why」で止まって、try-catch で例外を握りつぶしていたらどうなっていただろう?

アプリは落ちなくなるかもしれない。でも、裏で無駄な通信が走り続け、メモリを食い、いつかまた別の形で(例えばデータが二重に表示されるとか)バグが顔を出す。

海外の現場では、エンジニアの評価は「どれだけコードを書いたか」だけでは決まらない。

「どれだけ将来の負債(Tech Debt)を減らしたか」

「どれだけチーム全体の品質基準(Standard)を引き上げたか」

これが強烈に問われる。

「5 Whys」を使って根本原因まで辿り着き、それを「仕組み」で解決する提案をした時、同僚やマネージャーは初めて君を「ただのコーダー」ではなく、「プロフェッショナルなエンジニア」として認めてくれる。

「Mike、バグは直したよ。それだけじゃない。今後このチームで二度と同じバグが起きないように、新しいCommandクラスを作ったんだ。見てくれ。」

こう言った時のMikeの反応(「Dude, that’s awesome!」)こそが、海外で働くエンジニアにとって最高の報酬なんだ。

しかし、この「5 Whys」には副作用もある。

深く掘り下げることは、時にチームの「痛いところ」を突くことになるからだ。

「誰がこのクソコード書いたんだ?」という犯人探し(Blame Game)になってはいけない。あくまで「プロセス」を攻めるんだ。

次回の【転】では、この分析結果を実際にチームに持ち込んだ時、文化の違いやコミュニケーションの壁がどう立ちはだかるか、そしてそれをどう乗り越えて「解決」へと導くか。

人間臭い、ドロドロとした(しかし重要な)ソフトスキルの話をしよう。

コードは直せても、人の心と組織の習慣を直すのは、もっと難しいバグだからね。

会議室の決闘:正論というナイフで人を刺さないために

〜「Why」が「Who」に変わる瞬間、プロジェクトは崩壊する〜

バグの原因は特定できた。修正コード(Fix)も書いた。

再発防止策としての「非同期コマンドのガイドライン化」という素晴らしいアイデアも手元にある。

日本にいた頃の僕なら、ここで満足していただろう。

Wikiにひっそりとドキュメントを書き、「ガイドライン更新しました、各自読んでおいてください」とSlackに流して終わり。

だが、ここは海外だ。自己主張の激しいエンジニアたちが、それぞれの哲学を持ってコードを書いている戦場だ。

「読まれないドキュメント」は「存在しない」のと同じだ。

僕が直面したのは、コードのバグよりも厄介な**「ヒューマン・バグ(人間関係の軋轢)」**だった。

「Post-Mortem(検死会議)」の重苦しい空気

バグ修正の翌日、チームは「Post-Mortem」と呼ばれる振り返りミーティングを開いた。直訳すると「検死」。起きた障害の原因を分析し、再発防止を議論する場だ。

メンバーは5人。

PM(プロジェクトマネージャー)のSarahはスケジュールの遅れを気にしている。

シニアエンジニアのDaveは、腕は超一流だが、自分のコードに絶対の自信を持っており、プロセス変更を嫌う頑固者だ。

そして、QAのMikeと、新入りのジュニア、そして僕。

僕は意気揚々と、ホワイトボード(昨今はMiroだが)に昨夜の「5 Whys」の分析結果を描き出した。

「……というわけで、今回のクラッシュの根本原因は、個人のミスではなく、我々のアーキテクチャに『キャンセル処理』の標準実装が欠けていることにあります。だから、全ての非同期コマンドを、僕が作った新しい基底クラスに置き換えるべきです」

完璧なプレゼンだと思った。論理的で、隙がない。

しかし、返ってきたのはDaveの冷ややかな一言だった。

“That sounds like over-engineering based on one edge case.”

(たった一つのレアケースのために、過剰設計(オーバーエンジニアリング)をしてるように聞こえるな。)

彼は続けた。

「キャンセル処理が必要なのはわかる。だが、既存のコードを全部書き換えるリスクはどうする? お前の新しいクラスにバグがない保証は? そもそも、開発者が気をつければ済む話だろう? なぜ全員のコーディングスタイルを強制されなきゃならないんだ?」

空気が凍る。

僕は言葉に詰まった。英語のハンデも少しあるが、それ以上に**「正論が通じない」**ことへのショックが大きかった。

「5 Whys」が「尋問」に変わるとき

ここで僕は、海外に来て誰もが一度はハマる落とし穴に気づいた。

「5 Whys」は強力な武器だが、使い方を間違えると**「凶器」**になるということだ。

僕のプレゼンは、論理的には正しかったかもしれない。

しかし、Dave(彼はおそらく、過去にそのコピペ元のコードを書いた張本人だったのだ)にとっては、こう聞こえていたはずだ。

  • Why? → お前のコードが古いからだ。
  • Why? → お前がベストプラクティスを知らないからだ。
  • Why? → お前がチームの足を引っ張っているからだ。

「なぜ(Why)」を追求しているつもりが、いつの間にか**「誰(Who)」を責める尋問**になっていたのだ。

海外のエンジニアはプライドが高い。プロフェッショナルとしての自尊心を傷つけられた瞬間、彼らは「防御モード」に入り、どんな正論も弾き返すようになる。

これは日本でも起こりうることだが、直接的な物言いを好む英語圏では、対立がより鮮明に、鋭角に現れる。

「論破」しようとすればするほど、チームは分断される。

僕が必要としていたのは、論理的な「正解」ではなく、彼らを動かすための「政治力(Politics)」と「共感(Empathy)」だった。

作戦変更:Egoless Programming(自我を排したプログラミング)

その夜、僕はジェラルド・M・ワインバーグの名著『プログラミング心理学』の一節を思い出していた。

“Egoless Programming”(自我を排したプログラミング)。

自分のコードを自分自身と同一視してはいけない。そして、他人のコードを指摘する時は、人格ではなく事象にフォーカスせよ。

翌日、僕はDaveのデスク(というかZoomの個別チャット)に向かった。

戦うためではない。味方にするためだ。

「Hey, Dave。昨日の君の指摘について考えていたんだ。確かに『全てのコードを書き換える』というのはリスクが高いし、アグレッシブすぎたかもしれない。」

まずは相手の言い分を認める(Validate)。これが英語圏での交渉の基本だ。

「No」から入ってはいけない。「Yes, and…」だ。

「でも、僕は君のように経験豊富なエンジニアが、毎回手動で CancellationToken の処理を書くという退屈な作業に時間を使うのは、才能の無駄遣いだと思うんだ。僕が提案したいのは『強制』じゃなくて、『楽をするためのツール』の提供なんだよ。」

僕はアプローチを「品質管理(Quality Control)」から**「開発者体験(Developer Experience)」**へと切り替えた。

「この新しいクラスを使えば、君はボイラープレートを10行書く代わりに、1行書くだけで済む。もし気に入らなかったら使わなくてもいい。でも、これを使えば、君が嫌いな『QAからのつまらないバグ報告』は確実に減るはずだ。金曜日の午後にデバッグしなくて済むようになるんだぜ?

Daveの表情が緩んだ。

「金曜のビールが美味くなるなら、話は別だな」

文化の壁を超える「共通言語」

ここで重要だったのは、「Why」の矛先を変えたことだ。

「なぜミスをしたのか?」という過去へのWhyではなく、「なぜ我々はこれをするのか?」という未来へのWhy(目的)に焦点を当てた。

  • × なぜ古いコードを使っているのか?(過去・責任追及)
  • ○ なぜ新しいツールを導入するのか? → 我々が楽をして、クリエイティブな仕事に集中するためだ。(未来・利益共有)

海外の現場では、”For the company”(会社のため)や “For the quality”(品質のため)というお題目は、時としてあまり響かない。

それよりも、”What’s in it for me?”(私に何のメリットがあるの?) という問いに明確に答える方が、人は動く。合理的でドライに見えるかもしれないが、これこそがプロフェッショナル同士の「共通言語」なのだ。

小さな成功(Small Win)を積み上げる

Daveの合意を取り付けた僕は、全機能の一斉書き換えという無謀な計画を捨てた。

代わりに、**「ボーイスカウト・ルール」**をチームに提案した。

“Leave the campground cleaner than you found it.”

(来た時よりも美しくして帰ろう。)

つまり、既存のコードを一気に直すのではなく、「そのファイルを触る用事があったついでに、新しいパターンに書き換える」というルールだ。これならリスクは分散され、スケジュールへの影響も最小限で済む。

この提案は、PMのSarahにも受け入れられた。

「リファクタリングのためのスプリント」を確保するのは難しいが、「機能開発のついでに行う改善」なら許容範囲内だからだ。

そして数週間後。

以前なら複雑な非同期処理が必要だった新機能の実装で、ジュニアエンジニアが僕の作った基底クラスを使い、バグなしで一発実装を決めた。

コードレビューでDaveがコメントを残した。

“Nice and clean. Good job.”

その一言を見た時、僕はOutputウィンドウの「Build Succeeded」の文字を見るより遥かに大きな達成感を感じた。

システムという「機械」だけでなく、チームという「有機体」のバグを修正できた瞬間だったからだ。

転の教訓:正しさは「優しさ」で包んで初めて機能する

「5 Whys」は、真実を暴く鋭いナイフだ。

だからこそ、それを振り回す時は、細心の注意が必要になる。

特に、言葉のニュアンスが伝わりにくい海外の現場では、

「Why did you do this?」(なんでこんなことしたの?)

という言葉は、容易に攻撃として受け取られる。

我々が目指すべきは、犯人を捜して吊るし上げることではない。

**「誰もがミスをしたくてもできない仕組み」**を作ることだ。

そして、その仕組みを導入する過程で、チームメンバーのプライドや立場を尊重し、「全員が得をするストーリー」を描くことだ。

技術力(Hard Skills)で原因を見つけ、人間力(Soft Skills)で解決策を定着させる。

この両輪が回って初めて、君は「替えのきかないエンジニア」になれる。

さて、これで事件は解決……と言いたいところだが、物語にはまだ続きがある。

この「なぜなぜ分析」の習慣が定着した後、僕のキャリアと人生に予想もしない「結末」が待っていたのだ。

「なぜ」の果てにあるもの:エンジニアが「哲学者」になる日

〜コードを書く手は止めても、思考だけは止めるな〜

あの「会議室の決闘」から半年が経った。

今の僕たちのチームは、以前とはまるで別物だ。

誰かがバグを見つけると、Slackには自然とこう書き込まれる。

「Fix(修正)はこれだ。でも、Root Cause(根本原因)は何だと思う?」

かつては「攻撃」と受け取られた「Why」という言葉が、今ではチーム共通の「知的好奇心」の合言葉になった。

Daveは相変わらず皮肉屋だが、コードレビューでは誰よりも鋭くアーキテクチャの欠陥(Not just syntax errors)を見抜いてくれる。新人の書いたコードに対しても、「なぜこのパターンを選んだ? こっちの方がメモリ効率がいい理由は説明できるか?」と、教育的な「Why」を投げかけている。

WPFの画面がフリーズすることも、謎のクラッシュに怯える金曜日もなくなった。

僕たちはようやく、「バグと戦う時間」を終わらせ、「未来の価値を創造する時間」を手に入れたのだ。

エンジニアの「市場価値」を決めるもの

この一連の経験を通じて、僕は海外で生き残るための決定的な真実に気づいた。

それは、「How(どう書くか)」を知っているエンジニアは作業者として雇われるが、「Why(なぜ書くか)」を語れるエンジニアはリーダーとして重用されるということだ。

C#の文法や、WPFのXAMLの書き方を知っている人は、世界中に何万人もいる。GitHub Copilotに聞けば、秒速でコードは生成される時代だ。

しかし、「なぜ、この設計にするのか?」「なぜ、この機能がビジネスに必要なのか?」「なぜ、今のプロセスではダメなのか?」

この問いに答え、周囲を巻き込み、カオスの中から秩序を生み出せる人間は、AIには代替できない。

僕があの時、単に try-catch でバグを握りつぶしていたら、僕は今でも「バグ修正係」のままだっただろう。

痛みを伴う「5 Whys」の深掘りを経て、チームの文化にメスを入れたからこそ、僕は「シニア・アーキテクト」というポジションへの切符を手にすることができた。

給料が上がったとか、タイトルがついたとか、そういう話じゃない(いや、それも大事だけどね)。

一番の報酬は、**「自分の仕事に対するコントロール権」**を手に入れたことだ。

誰かに言われた仕様をただ実装するのではなく、仕様そのものの「Why」を問い、より良い形を提案できる立場。

これこそが、エンジニアとして最も楽しく、最も自由な状態なんじゃないだろうか。

人生のデバッグにも効く「5 Whys」

そして、この思考法は、オフィスの外でも僕を救ってくれている。

海外で暮らしていると、時々どうしようもない孤独や、漠然とした不安(Imposter Syndrome)に襲われることがある。

「英語がうまく通じなかった」

「文化の違いで誤解された」

「日本の同期はもっと出世しているのに」

そんな時、僕は心の中で「ひとり5 Whys」をやるんだ。

  • 「なぜ、落ち込んでいる?」→ 会議でうまく発言できなかったから。
  • 「なぜ、発言できなかった?」→ 準備不足で自信がなかったから。
  • 「なぜ、準備不足だった?」→ 英語のドキュメントを読むのを後回しにしたから。
  • 「なぜ、後回しにした?」→ 完璧に理解しようとしてハードルを上げていたから。
  • 「なぜ、完璧を求めた?」→ 「間違ったことを言ってはいけない」という日本の文化的な呪縛(Root Cause)がまだ抜けていないからだ。

ここまで掘り下がれば、解決策はシンプルだ。

「完璧じゃなくていい。まずは30点でもいいから、口を開くことを目標にしよう」

そう思えた瞬間、心のモヤモヤ(Symptom)は晴れ、次はどうすればいいかというアクションプランが見えてくる。

コードも、組織も、そして人生も。

複雑に見える問題のほとんどは、絡まり合った糸だ。

「Why」というナイフで丁寧に切り分けていけば、必ず「解ける結び目」が見つかる。

読者へのメッセージ:思考の深さが、あなたの深さになる

日本にいるあなた。あるいは、これから世界へ飛び出そうとしているあなた。

日々の業務に忙殺され、「とりあえず動く修正」の誘惑に負けそうになることはあるだろう。

上司や納期というプレッシャーの中で、根本原因から目を逸らしたくなる日もあるだろう。

でも、そこで一歩踏みとどまってほしい。

そして、心の中で小さく呟いてほしい。

「待てよ、本当の犯人(Root Cause)はどこだ?」

その一瞬の思考の停止、その一回の深掘りが、今日のあなたを昨日よりも賢くする。

その積み重ねが、1年後、あなたを「替えのきかないエンジニア」へと進化させる。

僕たちが書いているのは、単なる if 文や for ループの羅列ではない。

その背後にある「意思」と「論理」こそが、僕たちの作品だ。

さあ、今日もVisual Studio(あるいはVS Code)を開こう。

バグは怖いものじゃない。それはシステムがあなたに語りかけている「メッセージ」であり、あなたがより優れたエンジニアになるための「招待状」なのだから。

Keep asking “Why”.

そして、良い旅を。

コメント

タイトルとURLをコピーしました