「それ、今すぐ全部作り直すべき」って言っちゃう前に。海外で俺が学んだ「レガシーシステム」との賢い付き合い方

その「正義感」、ビジネスを殺してないか?

やあ、みんな。海外(某ヨーロッパの片隅)で、かれこれ10年近くC#とWPFをいじくり回してるITエンジニアだ。こっちで設計だの開発だのやってると、まあ、色々ある。

日本から「海外でエンジニアとして働く」っていうと、どんなイメージを持つ?

最新のMacBookをカチカチさせて、スタンディングデスクで、多国籍な同僚とスマートに「Hey, how’s it going?」とか言いながら、ピカピカのマイクロサービスをKubernatesで華麗にデプロイ…みたいな?

わかる。わかるよ、その気持ち。俺も来る前はそう思ってた(笑)

でもな、現実はそう甘くない。

特に俺みたいにWPF、つまりデスクトップアプリの世界、それもエンタープライズ(業務系)のど真ん中にいると、待ってるのはキラキラしたWebフロントエンドの世界とはちょっと違う、渋くて、重厚で、時に「マジかよ…」と頭を抱えるような現実だ。

海外IT現場のリアル:「開けちゃいけない玉手箱」

俺がこっちに来て、最初に関わったプロジェクトの話をしよう。

ある大手物流企業の基幹システムだった。もちろんC#とWPF。前任のエンジニア(もう辞めて遠い国に帰っちまった)から引き継いだんだが、これがまあ、すごかった。

まず、アプリの起動に平気で3分かかる。

特定の画面を開こうとすると、5回に1回は「応答なし」で固まる。

デバッグしようとコードを開けば、1ファイルに5000行を超えるコードビハインド(WPFやってるヤツなら、このヤバさがわかるだろ?)、謎のstatic変数、意味不明なスレッド処理…。

「…これ、アーキテクチャってレベルじゃねーぞ」

まさに「技術的負債」のデパート。いや、技術的負債っていうか、もう「産業廃棄物」じゃないかと。

バックエンドのSQL Serverのストアドプロシージャなんて、暗号解読かと思ったね。

その時、俺の頭にピカッと閃いたことがある。

エンジニアなら誰でも一度は(いや、毎日)思う、あの魔法の言葉だ。

「これ、全部作り直した方が早くない?」

<h3>エンジニアの「正義」と「Rip and Replace」の甘い誘惑</h3>

そう、これだ。

「Rip and Replace」——「ぶっ壊して、作り直す」。

なんて甘美な響きだろう。

パフォーマンスが悪い?

バグだらけ?

機能追加が難しい?

「それは、元の設計がクソだからだ! 最新の技術(当時は.NET Core 3.1とかかな)で、クリーンアーキテクチャを採用し、MVVMを正しく実装し、DIコンテナを整備して、非同期処理(async/await)を完璧に使いこなせば、すべては解決する!」

俺は本気でそう思ってた。

これはエンジニアとしての「正義」だと。

こんな腐ったコードを放置しておくことこそ「悪」だと。

パフォーマンス問題の解決策は、根本的なシステムの再構築(=Rip and Replace)以外にない。これは、当時の俺にとって疑いようのない「神話」だったんだ。

海外のテックカンファレンスやブログじゃ、いつも「我々はモノリシックなレガシーを捨て、マイクロサービスに移行してハッピーになりました!」みたいな成功体験がキラキラと語られてる。

それを見て、「そうか、やっぱり作り直すのが正解なんだ!」と、ますますその神話を強固に信じ込んじまう。

君も、もし今、目の前のコードに絶望してるなら、同じことを考えてるんじゃないか?

俺は早速、なけなしの英語で提案書をまとめた。

「現状のシステムがいかにヒドイか」「これを最新の技術で作り直せば、起動は5秒、バグはゼロ、開発効率は3倍になる」と。

そして、意気揚々とマネージャーのデイブ(仮名)にプレゼンしたんだ。

最初の壁:「Why?」と「So What?」の嵐

俺の熱弁を聞き終わったデイブは、コーヒーを一口すすると、静かにこう言った。

「OK, Kenta(俺の名前)。素晴らしい提案だ。で、それはウチの会社にいくら利益をもたらすんだ?」

「え…?」

「いや、だから、君がやりたい『全面的な書き直し』にかかるコストは? 君の見積もりだと、開発に6ヶ月、テスターが3ヶ月、移行に1ヶ月…。ざっとエンジニア4人分の人件費(こっちのエンジニアは高いぞ!)と、移行リスク、全拠点のオペレーターへの再教育コストもかかるな。トータルで、まあ数千万円ってとこか」

「は、はい。でも、それが終わればシステムは生まれ変わって…」

「生まれ変わった後、ウチの売上は上がるのか?」

「…売上、ですか? いや、開発効率が上がるので、次の機能追加が早くなって…」

「『早くなる』じゃダメだ。『どれくらい』早くなる? それで、競合のX社より『何が』優位になるんだ? そもそも、今、このシステムが抱えてる最大の問題は『起動が遅い』ことか? それとも『バグが多い』ことか? いや、現場のオペレーター(サリー、とか)が一番困ってるのは、『特定Aの操作が直感的じゃない』ことかもしれないぞ?」

俺は頭をガツンと殴られた気分だった。

デイブが言ってたことを、今なら冷静に解説できる。

ビジネスが「全面的な見直し(Rip and Replace)」に抵抗するのには、当たり前だけど、超合理的な理由があるんだ。

1. 途方もないコスト(Cost)

エンジニアが見積もる「開発費」なんて、氷山の一角だ。

ビジネスサイドは、移行にかかる全コスト(再教育、インフラ、機会損失)をシビアに見てる。「その金があったら、営業を3人雇える」とか、「新しいマーケティングキャンペーンが打てる」とか、そういう天秤にかけてるわけ。

2. 計り知れないリスク(Risk)

これが一番デカいかもしれない。

**「動いているシステムは、それ自体が資産だ」**って考え方だ。

たとえそれがバグだらけで、起動が遅くても、何年も(時には10年以上も)ビジネスの根幹を支えてきた「実績」がある。

「新しいシステム」は、エンジニアにとっては希望だが、ビジネスにとっては「未知のバグ」が潜む爆弾でしかない。

「移行中に、10年分の顧客データが吹っ飛んだら、誰が責任取るんだ?」デイブは真顔でそう言った。

3. ビジネスの中断(Disruption)

「OK、作り直すのに6ヶ月かかるとして、その間、ライバルのX社が新機能をリリースしたら、ウチはどうするんだ? 6ヶ月間、ウチは新機能の開発を一切ストップするのか?」

そう、ビジネスは止まれない。

俺たちが「壮大な教会」を建ててる間にも、市場は待ってくれないんだ。

「起」のまとめ:俺たちは何と戦っているんだ?

俺は完全に打ちのめされた。

俺の「技術的な正義」は、ビジネスの「現実的な論理」の前に木っ端微塵に砕け散った。

「じゃあ、なんだよ! エンジニアは、このクソみたいなコードを、この遅いシステムを、ただ黙ってメンテナンスし続けるしかないのかよ!」

そう腐りかけた時、デイブが笑いながら言ったんだ。

「Kenta、君の熱意は買う。だが、フォーカスがズレてる」

「『全部作り直す』か『何もしない』か、そんな0か100かで考えるな。それはエンジニアの怠慢だ」

「俺たちの仕事は、コードを綺麗にすることじゃない。ビジネスの問題を、技術で解決することだ」

——「ビジネスの問題を、技術で解決する」

その言葉が、俺のエンジニア人生の第二章の始まりだった。

パフォーマンス問題は、本当に「全部作り直さないと」解決できないのか?

俺たちエンジニアが「神話」だと思い込んでいる「Rip and Replace」という幻想。

その幻想から抜け出し、コストとリスクを最小限に抑えながら、最大のビジネス価値を生み出す「戦略的な一手」とは何なのか?

この日、俺は「コードを書く人」から、本当の意味での「エンジニア」への第一歩を踏み出すことになったんだ。

犯人は「全部」じゃなかった。俺が本当に戦うべき「ボトルネック」の見つけ方

灰になったエンジニア

デイブ(マネージャー)の部屋から出た俺は、正直、頭がまっしろだった。

「ビジネスの問題を、技術で解決する…?」

「コスト? リスク? そんなの、いいもの作れば後からついてくるだろ…」

半分は本気でそう思ってたし、半分は「正論」で殴られてぐうの音も出ない自分にイラついていた。

こっちはC#とWPFのプロとして、技術的な「正しさ」を追求しに来たんだ。

WPFでやるべきじゃない「重すぎる処理」をUIスレッドでやってるから固まる。

データベースの呼び出しが同期的(ブロッキング)だから、その間ずっと「応答なし」になる。

原因はわかってる。わかってるから、「全部直そう」って言ってるんじゃないか。

「なのに、なんで…」

自分のデスクに戻って、その「産業廃棄物」ことレガシーコードをもう一度開く。

5000行のコードビハインド。相変わらずだ。

俺はふてくされて、言われたことだけをやる「つまらない」作業に戻った。

「はいはい、どうせ俺は、このクソみたいなコードに、誰も幸せにならない機能A-2を追加するだけの『コーダー』ですよ…」ってな。

海外まで来て、俺は何をやってるんだろう。

あのまま日本で、もっとマシなWeb系のモダンな開発をやってれば…なんて、弱気なことも考えた。

現場(ゲンバ)は、コードの中にはない

腐っていた俺に、デイブが声をかけてきた。

「Kenta、顔が死んでるぞ。で、サリーには会ったのか?」

「サリー…? ああ、例のオペレーターの…」

「まだです。どうせ、’何もかも遅い’って言うに決まってますよ」

デイブは、やれやれって顔で肩をすくめた。

「いいか。エンジニアが一番やっちゃいけないのは、『ユーザーはこう思ってるに違いない』と決めつけることだ。特に、お前みたいな『技術的に優秀な』エンジニアほど、その罠にハマる」

グサッ。

「いいから、行ってこい。お前の席は、今日からあそこのオペレーションルームの隅だ。自分の目で、何が起きてるか見てこい」

そう言われて、俺はノートPC片手に、しぶしぶオフィス棟の地下にある、だだっ広いオペレーションルームに向かった。

そこは、俺が普段いる静かな開発フロアとはまるで違う「戦場」だった。

ひっきりなしに鳴る電話、飛び交う(俺にはわからない)現地の言葉での怒号、そして、ガチャンガチャンと動く物流マシンの騒音。

その一角で、15台ほどのPCが並び、オペレーターたちが俺の作った(いや、俺が引き継いだ)WPFアプリを必死に叩いていた。

いた。サリーだ。

この道20年のベテランオペレーター。

俺は隅っこに席をもらい、邪魔にならないよう、彼女たちの仕事(と、俺のアプリの挙動)を、一日中、文字通り「観察」し始めたんだ。

3分間のコーヒーと、30秒間の地獄

まず気づいたこと。

朝イチのアプリ起動。例の「3分かかる」やつだ。

俺は「これが諸悪の根源だ!」と思ってた。

だが、現実は違った。

オペレーターたちは、出社するとまずPCの電源を入れ、俺のアプリのショートカットをダブルクリックする。

——そして、全員、当たり前のように席を立つんだ。

え?

彼女たちは、アプリが起動するまでの3分間を「朝のコーヒーを淹れる時間」として、完全にルーティンに組み込んでいた。

誰もPCの前でイライラしながら待ってなんかいなかった。

3分後、コーヒー片手に席に戻ってきた彼女たちは、ログイン画面にパスワードを打ち込み、普通に仕事を始める。

…マジか。

俺が「技術的に最悪だ」「ユーザー体験を著しく損なっている」と断罪した「起動の遅さ」は、彼女たちにとって「問題」ですらなかったんだ。

じゃあ、問題はどこにある?

俺は、サリーの横に立って、彼女の手元を凝視した。

彼女の仕事はシンプルだ。

「A」という画面でオーダーを受け付け、「B」という画面で在庫を確認し、「C」という画面で配送指示を出し、ラベルを印刷する。

「A」画面。まあ、ちょっとモタつくが、許容範囲。

「B」画面。これも、検索に数秒かかるが、サリーはその間に書類を確認してる。

問題は「C」画面だった。

配送先を選び、配送業者を選び…最後に、「ラベル印刷」ボタンを押す。

カチッ。

その瞬間、アプリが完全に固まった。

「(応答なし)」

マウスカーソルが「砂時計(いや、WPFだから青いクルクルか)」になる。

サリーは、その間、微動だにしない。

キーボードから手を離し、じっと画面を見つめている。

5秒…10秒…15秒…。

イライラする。見ているこっちがイライラする。

この「待たされてる」感は、起動時の「どうせかかるとわかってる」待ち時間とは、質の違うストレスだ。

20秒…25秒…。

そして、30秒が経過した頃、ガシャン!と隣のプリンターが動き出し、ラベルが吐き出された。

同時に、アプリの「(応答なし)」が消え、操作可能になる。

サリーは、それを当然のようにひったくり、次の伝票に取り掛かる。

そして、数分後、また「ラベル印刷」ボタンを押し、あの地獄の30秒間が始まる。

俺はサリーに聞いた。

「サリー、このアプリで一番イライラするの、どこ?」

彼女は、俺を睨みつけるように(マジで怖かった)言った。

「あんた、エンジニアだろ? わかんないの?」

「このクソッタレな『印刷』だよ!」

「これ、1日に何回やるんですか?」

「さあね。忙しい日なら、200回は超えるんじゃない?」

…計算してみろ。

30秒 × 200回 = 6000秒

6000秒 = 100分

彼女は、1日の勤務時間のうち、1時間40分も、ただ「応答なし」の画面を待つためだけに、給料をもらってることになる。

これが、デイブの言ってた「ビジネスの問題」だったんだ。

幻想(Rip and Replace)からの目覚め

俺は、自分のデスクに(いや、オペレーションルームの隅の席に)戻って、震える手でコードを調べ始めた。

ターゲットはただ一つ。「C」画面の「ラベル印刷」ボタンのクリックイベントだ。

俺の「全部作り直す」という壮大な計画は、この時点で完全に消え去っていた。

そんなものは、エンジニアの自己満足で、現場のサリーの苦しみを何一つ理解していない、傲慢な「幻想」だった。

俺が戦うべき相手は、「5000行のコードビハインド」でも「古い.NET Frameworkのバージョン」でもない。

**「サリーの1日を100分間も奪い続けている、あの『30秒』」**だ。

なぜ30秒もかかる?

印刷ボタンを押した瞬間に、UIスレッドで何をやってる?

もしかして、データベースへの複雑な書き込みと、外部API(配送業者)へのHTTPリクエストと、プリンターへのバイナリデータ送信を、全部「同期処理」で、直列にやってるんじゃないか…?

そうだ。

フォーカスを絞れ。

「全部」作り直すな。「問題」を殺せ。

ラディカル(急進的)な変革じゃない。

サージカル(外科的)な改善だ。

俺は、ようやくエンジニアとしての「本当の仕事」の入り口に立った気がした。

「全面改修」を「3日」で超えた、たった一行の魔法 (async/await)

オペレーションルームが、俺の開発室になった

オペレーションルームの隅っこ。そこが俺の新しい「開発室」になった。

隣ではサリーが相変わらず「30秒のフリーズ」と戦い、電話のベルが鳴り響いている。

最高の環境だ。だって、俺が今からやろうとしてる「手術」の結果が、リアルタイムでわかるんだから。

もう俺の頭に「Rip and Replace」の文字はなかった。

あるのは、あの「C」画面の「ラベル印刷」ボタン(Button_PrintLabel_Click)だけだ。

俺は Visual Studio の診断ツール(プロファイラ)を起動した。

こういう時、勘で「ここが遅い」と決めつけるのは三流だ。俺は「承」で一度失敗してる。

データ(事実)こそが正義だ。

アプリをデバッグ実行し、印刷ボタンを押す。

——待つ。地獄の30秒。

——終わった。

プロファイラの結果を見る。

CPU使用率のグラフが、ある一点で天井に張り付いている。

「UIスレッド」が、100%占有されている。ビンゴだ。

どのメソッドが犯人か?

呼び出しツリーを掘っていくと、案の定、Button_PrintLabel_Click がすべての元凶だった。

こいつが、イベントハンドラ(WPFではRoutedEventArgsを受け取るアレだ)の中で、何をやってるか、コードを追った。

C#

// マジでこんなコードだった(簡略化してるけど)
private void Button_PrintLabel_Click(object sender, RoutedEventArgs e)
{
try
{
// 1. まずUIから値を取得
var orderId = txtOrderId.Text;
var shipper = cmbShipper.SelectedItem as Shipper;

// 2. データベースに(同期的に)接続して、詳細情報を取得
// これがまず遅い(約5秒)
var orderDetails = dbContext.GetOrderDetails(orderId);

// 3. 配送業者APIに(同期的に)リクエストを送信
// これが最悪。タイムアウトギリギリ(約15秒)
var trackingNumber = apiGateway.RequestTrackingNumber(orderDetails, shipper);

// 4. ラベルデータを(同期的に)生成して、プリンタに送信
// これも地味に重い(約10秒)
var labelData = labelGenerator.CreateData(trackingNumber, orderDetails);
printerDriver.Print(labelData);

// 5. 終わったらUIに反映
txtStatus.Text = "印刷完了!";
}
catch (Exception ex)
{
// エラー処理(雑)
MessageBox.Show(ex.Message);
}
}

絶句した。

これ、C#の教科書があったら「絶対にやっちゃいけない例」として載るレベルだ。

UIスレッドで、データベース接続、外部APIへのHTTPリクエスト、重いデータ生成、プリンタI/O…

「同期処理の満漢全席」かよ。

そりゃ30秒(5 + 15 + 10)固まるわ。

UIスレッドがこいつらの処理を「全部」律儀に待ってるんだから、アプリが「応答なし」になるのは当たり前だ。

6ヶ月の見積もり vs 30分(と、数行のコード)

「全部作り直す」計画では、俺はこれを「クリーンアーキテクチャだ!」「DIだ!」「リポジトリパターンだ!」とか言って、根本から作り直そうとしていた。

見積もりは6ヶ月。

だが、今、俺が戦うべき相手は明確だ。

「この Button_PrintLabel_Click メソッドを、UIスレッドから解放する」

ただ、それだけだ。

俺は深呼吸して、キーボードを叩いた。

まず、メソッドのシグネチャ(定義)を変える。

private void を private async void に。

C#

// async void は通常避けるべきだが、イベントハンドラは例外だ。
private async void Button_PrintLabel_Click(object sender, RoutedEventArgs e)
{
// ...
}

これだけで魔法が使える準備が整った。

次に、UIスレッドをブロックしている「犯人たち」を、バックグラウンドスレッドに叩き落とす。

Task.Run を使ってな。

そして、処理が終わるのを「待つ」が、UIスレッドは「ブロックしない」。

そう、await の出番だ。

C#

private async void Button_PrintLabel_Click(object sender, RoutedEventArgs e)
{
// 0. まず、ユーザーに「待ってね」と伝える
// ボタンを無効化し、スピナーを回す
this.IsEnabled = false; // 画面全体を無効化
spinner.Visibility = Visibility.Visible;

try
{
// 1. UIスレッドでしかできないことは、先にやる
var orderId = txtOrderId.Text;
var shipper = cmbShipper.SelectedItem as Shipper;

// 2. 3. 4. の重い処理を、ごっそりバックグラウンドへ
// (ここが外科手術の核心!)
var resultMessage = await Task.Run(() =>
{
// --- ここから下は、UIスレッドとは別のスレッドで動く ---

// 2. データベース処理(5秒)
var orderDetails = dbContext.GetOrderDetails(orderId);

// 3. 外部API処理(15秒)
var trackingNumber = apiGateway.RequestTrackingNumber(orderDetails, shipper);

// 4. 印刷処理(10秒)
var labelData = labelGenerator.CreateData(trackingNumber, orderDetails);
printerDriver.Print(labelData);

// --- ここまでバックグラウンド ---

// 戻り値だけ返す
return "印刷完了!";
});

// 5. バックグラウンド処理が終わったら、UIスレッドに自動で戻ってくる
// (これが await のすごいところ)
// UIに結果を反映
txtStatus.Text = resultMessage;
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
finally
{
// 6. 成功しようが失敗しようが、必ずUIを元に戻す
spinner.Visibility = Visibility.Collapsed;
this.IsEnabled = true; // 画面を再度有効化
}
}

書けた。

変更したのは、async void の追加、Task.Run と await の追加、そして try…finally でのUI制御。

コードの本質は何も変えていない。

設計も、アーキテクチャも、何もかも元のままだ。

かかった時間、わずか30分。

運命のデプロイ

「…よし」

俺はビルドし、テスト環境にデプロイした。

そして、隣の席のサリーを呼んだ。

「サリー、ちょっと試してくれないか」

「あら、エンジニアさん。何を直したの? あの起動の遅さ?」

「いや、こっちだ」

俺は「C」画面の「ラベル印刷」ボタンを指差した。

サリーは「どうせ同じでしょ」と鼻で笑いながら、いつものようにボタンをクリックした。

カチッ。

次の瞬間、サリーが「え?」と声を上げた。

アプリが、固まらない。

代わりに、画面の中央に俺が仕込んだスピナー(クルクル回るやつ)が「処理中…」という文字と共に表示されている。

マウスカーソルは自由に動く。なんなら、別のウィンドウ(電卓とか)も触れる。

アプリが「死んで」いない。

サリーは、信じられないという顔で画面と俺の顔を交互に見てる。

そして、30秒後。

ガシャン!とプリンターが動き、ラベルが吐き出された。

同時に、スピナーが消え、「印刷完了!」の文字が表示された。

サリーは、もう一度、ボタンを押した。

また、固まらない。スピナーが回る。

30秒後、ラベルが出てくる。

彼女は、3回、4回と、夢中になってボタンを連打(はできない、IsEnabled = false にしてるからな)し、そのたびにフリーズしないアプリの挙動を確認していた。

やがて彼女は、満面の笑みで俺の肩を叩いた。

「Kenta! あんた、神か!?」

やった。

6ヶ月と見積もった「全面改修」がやろうとしていた「パフォーマンス改善」という名の”結果”を、俺はたった30分の「外科手術」で(ほぼ)達成してしまったんだ。

本当の「改善」は、ここからだ

この時点で、俺は「転」の目標を達成した。

「30秒のフリーズ」という最悪のユーザー体験は、「30秒の待ち時間(ただしUIは応答性を保つ)」という、許容可能な体験に変わった。

これだけでも、サリーのストレスは激減だ。

だが、俺はもう「コードを書く人」じゃない。

デイブの言った「エンジニア」だ。

俺はデイブに報告に行った。

「デイブ、サリーの30秒フリーズ、直しました。UIを非同期化したんで、もう固まりません」

デイブは「よくやった」と頷いた。

「で? Kenta。その『30秒』は、短くならないのか?」

…きた。

俺が待ってた言葉だ。

「なります。なりますよ、デイブ」

「プロファイルは済んでます。犯人はわかってるんです」

俺は「外科手術」の第二段階(セカンド・オペ)に取り掛かった。

Task.Run の中身だ。

犯人1:外部API呼び出し(15秒)

コードをよく見たら、このAPI、毎回「認証トークン」を取得するために別のAPIを(同期的に)呼び出していた。アホかと。

このトークン、有効期限が1時間もある。

MemoryCache を使って、トークンを1時間キャッシュするようにした。

これだけで、API呼び出しは 15秒 → 1秒 になった。

犯人2:データベースアクセス(5秒)

GetOrderDetails が発行してるSQLクエリを見たら、巨大なテーブル同士を WHERE句で結合してるのに、インデックスが全く効いてなかった。

これは俺の専門外だ。DBA(データベース管理者)のミゲルに相談に行った。

「ミゲル、このクエリ、インデックス追加してくれない?」

ミゲルはSQLを見て「OK」と一言。30分後、インデックスが追加された。

結果、データベースアクセスは 5秒 → 0.5秒 に。

犯人3:印刷ジョブ生成(10秒)

これは手強かった。レガシーな印刷ライブラリが、内部で重いビットマップ処理をしていた。

だが、よく見ると、これも毎回「会社のロゴ」とか「定型文」を動的に生成している。

「んなもん、テンプレート化しとけよ!」

俺は、変更されない部分をあらかじめテンプレート(XPSファイル)として保持し、可変部分(追跡番号とか)だけを差し込むようにロジックを変更した。

結果、ジョブ生成は 10秒 → 2秒 に。

手術、完了。

かかった時間は、調査と実装、ミゲルとの連携含めて、トータルで3日間。

さあ、どうなった?

30秒(5 + 15 + 10)かかっていた処理は…

3.5秒(0.5 + 1 + 2)になった。

「神」から「ビジネスパートナー」へ

もう一度、サリーに試してもらう。

ボタンを押す。

スピナーが回る。

1、2、3…

ガシャン!

ラベルが吐き出された。

サリーは、もう笑ってなかった。

キョトンとして、PCとプリンターを見比べている。

「…Kenta。これ、バグってない? 早すぎない?」

俺はニヤリとして言った。

「これが、本来のスピードだよ」

サリーの1日の待ち時間は、

(改善前)30秒 × 200回 = 100分

(改善後)3.5秒 × 200回 = 11.6分

1日あたり、約88分(約1時間半)の時間が生まれた。

彼女は、その時間で、もっと多くのオーダーを捌いたり、後輩の教育をしたりできる。

これこそが「ビジネス価値」だ。

デイブは、俺の出した改善レポート(もちろん、削減できた工数=コストを明記した)を見て、こう言った。

「Kenta。君の見積もってた『全面改修(数千万円)』より、君の『3日間の仕事』の方が、よっぽど価値がある。ようこそ、本当のエンジニアリングの世界へ」

俺は、レガシーコードを憎むのをやめた。

それは「悪」じゃない。「現実」だ。

俺たちの仕事は、その「現実」と向き合い、技術という「メス」で、最も効果的な一点(ボトルネック)を、最小限のコストで切り裂き、ビジネスという「患者」を救うことだったんだ。

コードを憎むな、ボトルネックを憎め。俺たちが売るべきは「技術」ではなく「価値」だ

あの「手術」から、半年が過ぎた

あれから半年。

俺の戦場だったオペレーションルームは、驚くほど静かになった。

いや、電話の音や物流マシンの騒音は相変わらずだ。

だが、「キーボードを叩きつける音」と「ため息」が劇的に減った。

サリーは、今や俺のアプリの「最強のテスター」であり「最大の理解者」だ。

「Kenta、ここの検索だけど、昨日からちょっと遅くない? 多分、新しく入った商品のデータがインデックスされてないんじゃない?」

なんて、的確なフィードバックまでくれるようになった。

デイブは、あの「3.5秒への改善レポート」を役員会に提出した。

「Kentaの3日間の仕事が、オペレーター1人あたり年間XX時間(88分×勤務日数)の工数を生み出した。これは人件費YY万円の削減、あるいはZZ件の受注機会の創出に相当する」

——そう報告されたらしい。

おかげで俺は、次のプロジェクトで「技術選定の自由(と、ちょっとの予算)」を手に入れた。

あの時、「全部作り直させろ!」と駄々をこねていたら、今頃どうなっていただろう?

きっと、デイブとの信頼関係は崩壊し、サリーは今も30秒のフリーズと戦い、俺は「あいつは理想ばかり語る、使えないエンジニアだ」とレッテルを貼られて、腐っていたに違いない。

エンジニアが陥る「2つの幻想」

この一件で、俺は海外でエンジニアとして生きていく上で、絶対にハマってはいけない「2つの幻想」に気づかされた。

これは、日本から意気揚々とやってくる「技術に自信ニキ」なエンジニアほど、陥りやすい罠だ。

幻想1:「Rip and Replace(全部作り直し)神話」

まず、これ。俺が最初にハマったやつ。

なぜ俺たちは、あんなに「全部作り直したく」なるんだろう?

  • 技術的な好奇心: 「あの新しいフレームワークを使いたい」「クリーンアーキテクチャを試したい」
  • 既存コードへの軽蔑: 「こんな汚いコード、触りたくない」「書いたやつの顔が見たい」
  • ヒーロー願望: 「俺が、この腐ったシステムを救ってやる」

わかる。痛いほどわかる。

だが、それは「エンジニアのエゴ」でしかないことを、俺はデイブに叩き込まれた。

ビジネスの世界、特に俺がいるようなエンタープライズの現場では、「動いているコード」は、それがいかに汚かろうと「資産」であり「正義」だ。

それを捨てる「Rip and Replace」は、「コスト、リスク、ビジネスの中断」という3つの巨大な悪魔を同時に召喚する禁呪なんだ。

俺たちがやるべきは、その「資産」を活かしつつ、最小限のコスト(=最小限のコード変更)で、最大のビジネス価値(=サリーの笑顔と削減できた時間)を引き出すこと。

「戦略的エンハンスメント」——。

デイブはそう呼んでいたが、俺に言わせりゃ「外科手術」だ。

患者(ビジネス)を殺さず、患部(ボトルネック)だけを正確に切り取る。

それこそが、ベテランのエンジニアに求められる技術(スキル)なんだ。

幻想2:「俺の仕事はコードを書くこと」という思い込み

これもヤバい罠だ。

俺はC#とWPFのプロだ。だから、美しいXAMLを書き、効率的なC#のコードを書くことが仕事だと思っていた。

間違いだ。

海外(特に北米やヨーロッパ)の現場で求められるのは「コーダー」じゃない。

「エンジニア」であり、「プロブレム・ソルバー(問題解決者)」だ。

C#も、WPFも、async/awaitも、Task.Runも、全部「道具」でしかない。

大事なのは、その道具で「誰の」「どの痛み」を取り除くか。

俺は、サリーの「30秒のフリーズ」という「痛み」を、async/awaitという「道具」で解決した。

もし、あの問題が「非同期化」で解決できないなら、俺は(やりたくなくても)レガシーな BackgroundWorker を使ったかもしれないし、最悪、サリーのPCのスペックを上げる(ハードウェアで殴る)ことを提案したかもしれない。

手段(技術)にこだわるな。

目的(問題解決)から目をそらすな。

海外で「得する」エンジニアになるための3つの人生術

これから海外でエンジニアとして働こうとしている君に、俺がこの「産業廃棄物」から学んだ、マジで「得する」人生術を3つだけ共有したい。

人生術1:「まず、現場(Gemba)に行け」

これはもう、耳タコかもしれないが、真理だ。

トヨタ生産方式でいう「現地現物」ってやつ。

俺は、自分のデスクで「起動が3分もかかる!これが最大の問題だ!」と決めつけていた。

でも、現場のサリーは、それを「コーヒータイム」として受け入れていた。

「ユーザーは自分たちが本当に何を欲しているかわかっていない」

——こんな傲慢なセリフを吐く前に、まず現場に行け。

ユーザーが「何を言っているか」を聞くな。

ユーザーが「何をやっているか」「何で手が止まっているか」「どんな表情でため息をついているか」を、自分の目で観察しろ。

コードの中に「答え」はない。

答えは、いつだって「それを使っている人」の、眉間のシワの中にある。

人生術2:「完璧な100%」より「致命的な80%」を殺せ

パレートの法則(80:20の法則)ってやつだ。

「問題の80%は、たった20%の原因によって引き起こされている」

あのシステムは、今も90%以上が「産業廃棄物」のままだ。起動も相変わらず3分かかる。

でも、それでいいんだ。ビジネスは絶好調で回ってる。

俺がやったのは、サリーのストレスの80%を占めていたであろう「ラベル印刷の30秒フリーズ」という「たった一つの原因」を見つけ出し、殺したことだ。

使った労力は、プロジェクト全体から見れば1%にも満たない「3日間」だ。

全部を綺麗にしなくていい。

全部を最新化しなくていい。

一番痛いところ、一番ビジネスの血が流れているところだけを見つけて、そこだけをピンポイントで叩け。

それが、最小のコストで最大のリターンを生む、エンジニアの「コスパ最強」の働き方だ。

人生術3:「技術的負債」は「ビジネス価値」で返済しろ

俺たちは「技術的負債」という言葉が大好きだ。

「このコードは負債だらけだから、新しい技術(技術的資産)で返済すべきだ!」と息巻く。

だが、デイブのようなビジネスサイドから見れば、それは「借金(負債)を返すために、別の銀行でさらに大きな借金(新技術への投資)をしろ」と言ってるのと同じだ。

彼らが求めているのは「返済」そのものじゃない。

「キャッシュ(現金)」だ。

俺の「3日間の手術」は、サリーの88分の時間(=人件費というコスト削減)を生み出した。

これは、会社にとって明確な「キャッシュ」であり「利益」だ。

俺は、「技術的負債」を「技術」で返したんじゃない。

「技術」を使って「ビジネス価値(キャッシュ)」を生み出し、その結果として「負債」を(間接的に)返済したんだ。

このマインドセットを持ってるだけで、君のマネージャーからの信頼は爆上がりする。マジだ。

結論として

もし君が、今、目の前のレガシーコードに絶望して、「あーもう、これ、全部作り直した方が早くない?」と口走りそうになったら。

——一度、深呼吸してくれ。

君が戦うべき相手は、その汚いコードじゃない。

そのコードが引き起こしている「たった一つの、最も深刻なビジネス上の痛み」だ。

それを見つけ出すために、まず席を立て。現場に行け。

それを使っている人の「ため息」を聞け。

そして、君のC#やWPFの(あるいは他のどんな言語でもいい)最高の技術という名の「メス」で、そこだけを的確に、最小限の出血で切り取れ。

それができた時、君はもう、ただの「コーダー」じゃない。

国境を越え、言語の壁を越え、ビジネスから本当に必要とされる「プロフェッショナルなエンジニア」だ。

…さて、と。

俺も、次の「患部」を探しに、オペレーションルームのサリーにコーヒーでも淹れに行ってくるかな。

コメント

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