その「正義感」、ビジネスを殺してないか?
やあ、みんな。海外(某ヨーロッパの片隅)で、かれこれ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の(あるいは他のどんな言語でもいい)最高の技術という名の「メス」で、そこだけを的確に、最小限の出血で切り取れ。
それができた時、君はもう、ただの「コーダー」じゃない。
国境を越え、言語の壁を越え、ビジネスから本当に必要とされる「プロフェッショナルなエンジニア」だ。
…さて、と。
俺も、次の「患部」を探しに、オペレーションルームのサリーにコーヒーでも淹れに行ってくるかな。

コメント