最初にぶつかった“コードのカオス”と、そこからの気づき
正直に言うと、最初に海外でITエンジニアとして働き始めた頃、僕は完全に「自分は日本でそれなりに経験積んできたし、そこそこやれるはずだ」と思ってた。C#とWPFでの開発経験は日本でも数年あって、それなりに大規模案件もやってきたし、自信はあった。けど、最初の現場で僕を待っていたのは、想像以上にハードな「現実」だった。
入社初日、まず渡されたのは、ある既存WPFアプリケーションの機能追加タスクだった。画面はMVVMっぽい作りにはなってるけど、実装の中身はもう…ひどいものだった。
同じような処理があちこちにコピペされていて、イベントハンドラの中で直接DBアクセスしてる部分すらある。メソッド名は「DoWork1」「ProcessData2」みたいな意味不明なネーミングばかりで、正直どこがどのロジックを担当しているのか、最初は全く追えなかった。
「なぜこのコードはこうなってるの?」という素朴な疑問
最初の数日はただひたすら、既存コードを読んで理解しようとするだけで終わった。Pull Requestは作るけど、レビューでガンガン突っ込まれる。
ある日、思い切ってチームリードに聞いてみた。
僕:「この処理、3箇所にほぼ同じロジックあるけど、これってリファクタして共通化しちゃっていいのかな?」
リード:「Good catch! そうだね、実はそこずっと誰かが手を入れるの待ってたとこだよ。」
…え、まさかの放置案件。
理由を聞いてみたら、前任者は短期契約で急いで作ってそのまま去っていったらしい。誰もリファクタリングする時間もなく、次の人がそのままコピペして使ってきた、という負の連鎖。
この瞬間、自分の中でスイッチが入った。
「これは…自分がやるしかない。」
海外現場での“コード最適化”の進め方:日本と違う空気感
ただ、日本でやっていたときと明らかに違ったのは「いきなり全部リファクタしちゃダメ」というチーム文化。
日本の職場だと、「気づいたらすぐ直す」「ついでに全部キレイにしちゃう」みたいな空気があった。でもここでは違う。
リード:「いい?リファクタリングは常に『最小単位』で、『理由を明示』して、Pull Requestのスコープを絞るんだよ。じゃないと、レビューする側が追えなくなる。」
このアドバイスは、最初は少し違和感があった。
「良かれと思って大改修しようとしたら、めちゃくちゃ嫌がられる」というのは、実際にやってみてすぐわかった。
具体的には:
- 1つのPRで変更するのは、基本1つの目的だけ
(たとえば「この処理の共通化」だけ、とか「このViewModelのINotifyPropertyChanged実装の簡略化」だけ) - コミットメッセージは超具体的に
(例:「Extracted duplicate DB access logic into DataRepository class for reuse」) - 必ずユニットテスト追加、もしくは既存テストが落ちないことを保証
まず取り掛かった「小さなDRY」からのスタート
僕が最初に取り掛かったのは、3箇所にバラバラにあった「ある条件でDBからレコード取得→リストに変換→画面にバインド」という処理の共通化だった。
まずは以下のステップで進めた。
- 既存ロジックの完全読解
- 各箇所で使われているパラメータの違い
- 取得対象のテーブルやWHERE句の違い
- 呼び出し元のUIイベントトリガーの違い
- 仮リファクタのローカルテスト
- 一旦ローカルで共通メソッド化して、自分のローカル環境だけで画面動作確認
- 小さなPull Requestとして提出
- 影響範囲を明示
- Before/AfterのサンプルスクリーンショットをPRコメントに添付
- テストケースも最低限カバー
レビューでは最初、いくつか指摘が入った。
- 「このメソッド名だと用途が広すぎるから、もっと限定的な名前にしよう」
- 「ユニットテストないの?最低限、この条件とこの条件で動くことはテストして」
それでも数回の修正を経て、ようやくマージ。
その後、Slackでチーム全体にリードが投稿してくれた。
リード:「Hey team, check out Hiro’s latest PR! Great first step towards cleaning up the data layer. Let’s keep this momentum going!」
…正直、めちゃくちゃ嬉しかった。
ここで学んだ“海外式リファクタ文化”の第一歩
この最初の体験で学んだのは、「正義感で一気に大改修」はNGで、「小さな改善を積み重ねること」「ドキュメント化・テスト重視」「チームで合意形成をとる」これが海外現場でのリファクタの進め方だということ。
そして、ここからさらに、**「どうDRY原則やデザインパターンを適用していくか」**という本格フェーズに入っていくことになる。
本格的なリファクタ開始と、デザインパターン適用フェーズ
最初の小さな成功体験から、僕の中でリファクタリングへのモチベーションが一気に高まった。
ただ、もちろんここで浮かれて全部を一気にやろうとするとまた怒られる。
なので、次は「どうやって段階的に、かつ安全に、そしてチームの合意を得ながら進めるか?」というフェーズに突入した。
「どこから手を付けるか?」の優先順位付け
次のステップは、コードベース全体を見渡して「改善インパクトが大きい部分」「現実的に手を入れられる範囲」を見極めることだった。
そのために僕がやったのが以下の作業。
① コードメトリクスの収集
まず、Visual StudioのCode Metricsツールを使って、以下のデータを取得。
- メソッドごとのMaintainability Index
- Cyclomatic Complexity(循環的複雑度)
- 行数が異常に長いメソッド一覧
Excelにエクスポートして、簡単にスプレッドシート化。
「影響範囲が広くて、しかも複雑度が高い場所」にマーキング。
② チーム内レビューセッション
リードに提案して、チーム内で「どこを優先的にリファクタすべきか」レビューセッションを開催。
僕:「この辺りのモジュール、重複が多くてメンテナンス性が低いんで、まずはここから共通化進めたいと思ってます。」
リード:「いいね。でもその前に、この部分は来月から別チームが触る予定だから、そこに手を入れるのは控えて。」
他の同僚:「あと、今のSprintは新機能実装優先だから、リファクタ作業は‘Engineering Backlog’に入れて、次のIterationで進めよう。」
…そう、ここが日本と決定的に違う点。
どんなに正しい改善案でも、「今のSprintでやるべきか?」「このタイミングでやっていいか?」という優先順位管理が絶対ルール。
「正しいからすぐやる」じゃなくて、「計画に沿って、最小リスクで進める」が原則。
実際のデザインパターン適用:RepositoryパターンとService層導入
最初のターゲットに選ばれたのは「データアクセス層の整理」。
これまでのコードは、ViewModelやコードビハインドから直接SQL叩いてるものが多かった。
【Beforeの状況】
- 各画面ごとに、同じようなSQL文がバラバラに存在
- DB接続はほぼ全部
SqlConnectionを手動でOpen/Close - エラーハンドリングなし、トランザクション管理もない
- ユニットテスト不能
【適用したパターン】
1. Repositoryパターン
→ DBアクセス部分をすべて専用のRepositoryクラスにまとめ、
各エンティティごとにインターフェイス化。
public interface ICustomerRepository
{
Task<List<Customer>> GetCustomersAsync();
Task<Customer> GetCustomerByIdAsync(int id);
}
2. Service層導入
→ ビジネスロジック部分はServiceクラスに移し、ViewModelはそれを呼び出すだけの形に。
public class CustomerService
{
private readonly ICustomerRepository _repository;
public CustomerService(ICustomerRepository repository)
{
_repository = repository;
}
public async Task<List<Customer>> LoadCustomersAsync()
{
return await _repository.GetCustomersAsync();
}
}
3. Dependency Injection対応
→ 将来的なテストやMock対応を見据え、ServiceとRepositoryはコンストラクタインジェクションに変更。
4. Unit Testの追加
→ NUnit+Moqで、最小限のユニットテストもセットで作成。
【Afterの状態】
- ViewModelはビジネスロジック非依存に
- Repository単位でのテストが可能に
- SQL処理の重複がゼロに
- 将来的なDB変更にも強くなった
チームのリアクション:文化の違いを再確認
この変更をPull Requestとして出したとき、レビューコメントは本当に勉強になった。
- 「Nice work on introducing the Repository pattern! This will help a lot with future maintenance.」
- 「Please make sure to include a rollback plan, just in case we need to revert quickly after deployment.」
- 「Consider writing a small ADR (Architecture Decision Record) to document this design decision for future developers.」
…この**ADR(設計意思決定記録)**って文化、僕にとっては初体験だった。
単に「変更したよ!」だけじゃなく、「なぜこのデザインにしたのか?」「他に検討したパターンは?」「理由は?」という背景まで、ちゃんとドキュメントに残す文化。
「ただコードを書く」から「チームの技術資産を育てる」マインドへ
ここでようやく、海外現場での「リファクタの進め方」の本質に気づき始めた。
- ✔ コード変更は小さく刻む
- ✔ 常に理由を明示する
- ✔ 他人がレビューしやすい粒度でPRを出す
- ✔ ドキュメントで背景も残す
- ✔ 最低限のテストは必ずつける
このあたりから少しずつ、他のチームメンバーからも「Hiro、この部分も次お願いできる?」とか「来週の設計レビューでこのパターン採用について話してくれない?」と言われるようになってきた。
気づけば、リファクタリングやデザインパターン適用の“旗振り役”的ポジションになっていた。
そして次は、より大きなスケールでのリファクタ、「依存関係逆転」や「イベント集約」「MVVMアーキテクチャの再整理」といった、さらに難易度の高いフェーズに進んでいく。
大規模アーキテクチャ改善と、海外現場での合意形成バトル
Repositoryパターン、Service層導入、DI対応、そして小さなユニットテスト追加。
このあたりまでは正直「ウォームアップ」だった。
でもここからが本番。
僕に待ち受けていたのは、もっとスケールの大きい「アーキテクチャレベルのリファクタ」と、それに伴うチーム内政治バトルだった。
課題:MVVM崩壊と巨大化するViewModel
次に向き合うことになったのは、WPFアプリにありがちな「MVVM崩壊問題」。
つまり、ViewModelがどんどん肥大化して、ビジネスロジックとUIロジックがごちゃ混ぜになり、
テスト不能&保守不能になってる状態。
たとえば…
- 1つのViewModelに、3000行以上のコード
- APIコール、ローカルデータ変換、画面バインディング、UIイベントハンドラ、全部ごちゃ混ぜ
- 「LoadData」「OnClickButton」みたいな雑なメソッド名
- プロパティ数が50以上ある
「このままじゃ、誰も次の機能追加できない」
次のリリースで大きな新機能が追加される予定だったけど、
誰もこの巨大ViewModelに触りたがらない。
リードとの1on1ミーティングで僕は言った。
僕:「このまま進めたら、確実にバグ地獄になります。せめて、次のSprintの前にアーキテクチャ改善しませんか?」
リード:「…同意。ただ、タイミングが問題だ。今のスケジュール、かなりタイトだから。」
そう。
どんなに技術的に正しくても、ビジネス優先順位に勝てないのが海外プロジェクトのリアル。
ここからが、僕の「合意形成バトル」のスタートだった。
合意形成のためにやったこと:ドキュメント&デモ作戦
まず、ただ「やりたいです」「このままじゃダメです」だけじゃ誰も動かないことは分かっていた。
なので、以下の作戦を立てた。
【作戦1】ADR作成:「Why Refactor Now?」
「なぜ今リファクタが必要か?」を明確にするためのADR(Architecture Decision Record)を1枚書いた。
内容はこんな感じ。
- 背景
ViewModelが肥大化、単体テスト不可、次リリースで変更リスク大。 - 問題点
- コード変更時のバグリスク増大
- 新機能追加の工数倍増
- レビュー負担増
- 選択肢
- ① 今すぐリファクタを行う
- ② 今回はスルーして、次リリース後に対応
- ③ 現状維持(非推奨)
- 推奨アクション
今Sprintで、最低限のアーキテクチャ分割だけ実施。(Service層・UIロジック分離) - リスクとMitigation
- スケジュール遅延リスク → Scopeを明確に限定
- バグ混入リスク → テストカバレッジ強化で対応
【作戦2】ミニPoC(Proof of Concept)
さらに、口だけじゃなく「動くもの」で説得するため、
週末を使って既存巨大ViewModelの一部をサンプル分割して、
Service層とCommand層にリファクタして動作サンプルを作った。
これを使って、次のチームミーティングでプレゼン。
僕:「このサンプルでは、既存のLoadDataメソッドを完全に外部サービス化しました。これでユニットテストが通るし、将来のメンテが圧倒的に楽になります。」
同僚たちの反応はというと…
- 若手A:「おお…これなら確かにテスト書ける…」
- ベテランB:「パフォーマンス影響ない?」
- リード:「素晴らしい。でもスケジュールどうする?」
【作戦3】タイムボックス提案
ここで最後の一手。
「この作業、次Sprintの最初の3日間だけ、タイムボックスでやらせてほしい。
もし3日で想定通り進まなければ、すぐ元に戻す。」
これでようやく、リードから「Goサイン」が出た。
実作業:ステップバイステップでの大規模リファクタリング
合意が取れてからは、以下の流れで進行。
① ViewModel分割計画作成
- 各機能ごとに、「このロジックはどのレイヤーに移すか」マッピング。
- 新規クラス設計:
- Services
- Commands
- DTOs
- EventAggregator導入(Prismベース)
② 自動テスト作成
- NUnit+Moqで最低限のカバレッジ確保。
- 特に、データ取得ロジック、変換処理、エラーハンドリング周り。
③ チーム内リファクタガイド作成
- 「どういう場合にService層に移すべきか」
- 「UIロジックとビジネスロジックの分離基準」
- 「依存関係注入の具体例」
これをConfluenceにドキュメント化。
Slackでも「New Refactor Guidelines」チャンネル作成して、情報共有。
④ PR運用ルール変更
- 「ViewModel内のロジック追加は禁止」
- 「必ずService経由で呼び出すこと」
- 「ユニットテスト必須」
これらを、コードレビューチェックリストに追加。
想定外の壁:「抵抗勢力」の存在
もちろん、全員が賛成だったわけじゃない。
特にベテランの一部メンバーからは、こんな声が出た。
- 「今までのやり方で十分動いてるじゃん」
- 「余計な変更入れて、バグ出たらどうするの?」
- 「今後もこのルール守り続ける自信あるの?」
正直、ここの調整は一番ストレスだった。
僕がやったのは、とにかくデータで語ること。
- リファクタ前後でのテスト実行結果比較
- レビュー工数の削減実績
- 実バグ件数のBefore/Afterデータ提出
これを1ヶ月単位でまとめて、次のスプリントレトロスペクティブで発表。
その結果、少しずつだけど「やってよかった感」がチーム全体に浸透し始めた。
最後の仕上げ:「文化として根付かせる」
最終段階として、リードと協力して、
チームのCoding Standardsドキュメントに以下の項目を正式に追加。
- 「新規ロジックは必ずService層、Repository層で作成」
- 「UI層はプレゼンテーションロジックのみに限定」
- 「すべてのビジネスロジックはユニットテスト対象とする」
これで、単なる「一時的な改善活動」ではなく、**「文化」**として定着させることができた。
次のフェーズはさらに、CI/CDパイプラインでの自動コード解析や、SonarQube導入による静的解析の組み込みなど、「品質管理自動化」の段階に入っていくことになる。
変わったチームの空気、そして自分の成長
あの最初の一歩、「3箇所にコピペされていたSQL処理を共通化」から始まった僕のリファクタリング旅。
そこから数ヶ月、今振り返ると、チームの空気も、自分自身のエンジニアとしてのスタンスも、大きく変わっていた。
チームの変化:「改善」が日常の会話になった
まず、一番変わったのはチームメンバーたちの意識。
以前までは、「とにかく動けばOK」「次の機能追加が最優先」「リファクタなんて後回し」という雰囲気だった。
でも今では、日々のスタンドアップミーティングで自然にこんな会話が出るようになった。
- 「このメソッド、最近肥大化してきてるから、次Sprintで分割しようか」
- 「この新機能、最初からService層に切り出して作ろう」
- 「このあたり、テストカバレッジ足りないから、次リファクタタイミングで追加しよう」
しかも、以前は「リファクタやります」と言うと「え、なんで?今そんな余裕ある?」と突っ込まれてたのに、
今はむしろ「いいね、ちゃんとBacklogに入れておこう」「次SprintでScope入れる?」という反応が返ってくる。
Slackのチームチャンネルでも、みんなが自発的に「気づき」や「改善アイデア」をポストする文化ができた。
KPIにも変化が出た
もちろん、気持ちの面だけじゃない。
実際の成果としても、以下のような改善が見えてきた。
- Pull Requestレビュー時間の短縮
→ Before:約2日 → After:約0.5日 - 本番リリース後のバグ件数の減少
→ Before:月平均8件 → After:月平均2件 - ユニットテストカバレッジ
→ Before:15% → After:45%(まだ道半ばだけど、確実に進歩) - Onboarding速度
→ 新しいメンバーがプロジェクト理解にかかる時間が半分に
これらの数値は、マネージャー陣からも評価されて、
「このチームはプロセス改善に積極的で良いロールモデルだ」と、社内の全開発部門向けに紹介されたこともあった。
自分の変化:「コードを書く人」から「チームに価値を生む人」へ
何より、自分自身のマインドセットがガラッと変わった。
Beforeの自分:
- とにかく「早く、たくさんコードを書くこと」が正義
- リファクタは「気づいたときに勝手にやるもの」
- チーム内合意形成とか、あんまり気にしてなかった
- ドキュメント書くの面倒くさい
Afterの自分:
- 「この改善がチーム全体にどう影響するか?」を常に考える
- 改善活動は「技術」+「チーム運営」+「コミュニケーション」の三位一体
- ADRやテクニカルノートを書くことが、むしろ楽しくなった
- 後輩にリファクタ手法やパターン適用方法を教える立場に
自分でも驚くほど、**「エンジニアリングマネジメント」「テックリード的ポジション」**に興味が出てきた。
正直、数ヶ月前の僕なら想像もしなかった未来。
異文化チームで学んだ、海外ならではの「改善の進め方」
今回の経験を通して、一番感じたのは、
「技術的に正しいこと=すぐ通る」ではない、という海外現場の現実。
日本の現場では、リファクタリングって「上司に黙って勝手にやる」「気づいた人が空き時間にやる」ことが多かった。
でも、海外では完全に違った。
ここで僕が学んだ、海外現場でのリファクタ成功ルールをまとめるとこんな感じ。
【僕なりのグローバル現場・リファクタリング7か条】
- タイミングを見極めろ
→ ビジネス優先順位が最優先。空気を読まずに動くと逆効果。 - ドキュメントで説得しろ
→ ADR、Confluence、Jiraのチケットで「なぜ」「どこを」「どう改善するか」を可視化。 - データで語れ
→ Before/AfterのKPI、テストカバレッジ、バグ件数…定量データは最強の武器。 - 小さく始めろ
→ Scope creep(範囲拡大)厳禁。「今回はここだけ」に絞る。 - 仲間を巻き込め
→ 若手、リード、QAチーム…味方を作って進める。 - テストを書け
→ 「リファクタ後にバグが増えた」を防ぐには、テストが命。 - 継続せよ
→ 「一回きりのリファクタ」は意味がない。プロセスとして根付かせることがゴール。
最後に:これから海外で働く人へ伝えたいこと
もし、これを読んでいるあなたが、これから海外現場でITエンジニアとして働く予定なら、ぜひ覚えておいてほしい。
「技術力」だけじゃ、海外現場では生き残れない。
必要なのは、「周囲を巻き込む力」「合意形成力」「チームへの貢献姿勢」。
その上で、自分の技術を武器にして、少しずつ、確実に周りを巻き込んでいくこと。
そうやって、自分自身も、チームも、プロダクトも、少しずつ良くしていける。
僕がそれを経験してきたように。
だから、もし明日、あなたが既存コードの中で「なんだこのカオス…」って心が折れそうになっても、
それはむしろチャンスかもしれない。
それは、あなたが「チームを変える最初の一歩」を踏み出せるチャンスだから。

コメント