巨大な処理のリファクタリングにどのように向き合っているか

## はじめに

オープンロジのシステムは10年ものということもあり技術的負債が溜まってきており、開発生産性に影響を及ぼしています。今後の事業拡大に向けて現在負債解消に向けた機運が高まっており、全社的に取り組みを開始しています。
その中で私はアプリケーションの負債解消専門チームに所属しており、システムの心臓部である巨大で重要な処理「出庫依頼作成処理」のリファクタリングと向き合っています。
リファクタリング自体はまだまだこれからという感じですが、どのようなステップで実施しようとしているのか、今は何をやっているのか、どのような工夫をしているのかなど、社内で作成したスライド（以下のタイトル）の一部を抜粋・編集しながら書ける範囲で書いていきたいと思います。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2266459/e761f2b9-f274-01e3-1bd6-78228edfcc9d.png)

## 出庫依頼作成処理とは

オープンロジは主にEC事業者の方の荷物をお預かりして、提携倉庫で保管し、注文が入り次第倉庫から発送するという物流代行サービスを提供しています。その大まかな流れの中で、出庫依頼作成処理というのは倉庫に発送を依頼するデータを作成する処理です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2266459/815bd73f-031a-d87d-a6f3-a955d1c87dda.png)

そう聞くと大した処理じゃないんじゃない？という感じがするかもしれませんが、倉庫から商品を発送するためには決定しなければならないことやチェックしなければならないこと、作成しなければならない書類等が多くあります。

例えば商品についてであれば、在庫数チェック・在庫の確保は思い浮かぶかもしれませんがその他にもどの温度帯で送るのか？や賞味期限や製造年月日を考慮してどのロットのものを送るのが最適か？や、具体的にはどの倉庫のどの棚の商品を出庫するのか？など考えることはたくさんあります。
配送周りでいうと、どの配送キャリアで送るのが最適か？や、海外の場合はインボイスなど税関に必要な情報は足りているか？など、これまた考えることがたくさんあります。
また、ギフトラッピングや緩衝材など荷主やエンドユーザーが指定できるオプションも豊富に存在します。
そして、これらの要素は完全に独立しているわけではなく、商品が配送に影響してくるなど依存関係も持っています。

これらの処理が集約されたオープンロジの心臓部とも言える処理、それが「出庫依頼作成処理」なのです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2266459/d1013a04-b76f-c0dc-6288-b1cfb2d2b7e7.png)

## 出庫依頼作成処理の現状

上記で述べたように出庫依頼作成処理は様々な処理の集合体ですが、現状としては長年の積み重ねによりそれらが絡まっていくつも問題を抱えており、秘伝のタレのような状態になってしまっています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2266459/d16d2551-ee31-e463-b0a5-204f2b907070.png)

それにより仕様を把握したり処理を追加する難易度が上がり、結果的に組織の開発力を低下させる一因となってしまっています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2266459/d91c2dd9-4122-99ff-4a7b-75a37af03913.png)

## リファクタリングしたい！けれど簡単ではない

ということで、今後の事業拡大を見据えてリファクタリングしたい！となるわけですが、そう簡単な話ではありません。システムが扱っているドメインが非常に複雑である中で、有識者は限られています。また、クラス設計・テーブル設計などやりたいことはたくさんあるが、それらを進めると影響範囲は巨大でハイリスクです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2266459/b6b5b647-c3f5-5978-8084-dccf11ace091.png)

まずはどのように進めていくかから考える必要がありました。

## 慎重にステップを踏んで進めることにした

リスクを抑えつつリファクタリングを行うために少しずつステップを踏んで進める算段を立てました。5つのステップに分けて作業を進めていきます。ざっくり言うと「きちんと単体テストで動作を保証してからリファクタリングしようね」という王道の手順です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2266459/9ed79aa0-b2d0-d3c4-7533-5166065ce881.png)

### 1. 仕様整理

I/O、永続層の変化、バリデーションなどテストすべき仕様を整理します。また、この活動を通して単体テスト実装の担当である私が仕様にキャッチアップしたり、プロジェクト内で機能や仕様に関する共通認識を持ったりと、識者を増やして複雑なドメインを効果的にリファクタリングしていくための布石を打っておくような効果も狙っています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2266459/76048490-2ee0-b0e1-df8a-a54f51cfb710.png)

### 2. 単体テストを充実させる

「1. 仕様整理」で整理した仕様に従って単体テストを書いていきます。今までの単体テストは活かすのが難しい状態なので、新たに記載するテストだけで動作保証を目指していきます。
カバレッジだけでテスト品質は測れませんが、目安として分岐網羅率100%を目指します。
またポイントとして、リファクタリングによって単体テストが壊れてしまわないように、リファクタリングの対象となるであろう関数に対してはその関数の呼び出し元を通してテストするようにします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2266459/64c927b8-81d1-562f-d0e7-009dbe9bbd8f.png)

### 3. リファクタリング（インターフェースは変更しない）

いくつかの主要クラスのインターフェースは変更せずに内部のリファクタリングを行います。インターフェースを変更しない部分を最初に決めておき、それを前提に「2. 単体テストを充実させる」を行っているので、単体テストで動作が保証された状態でリファクタリングを行うことが可能となります。
また、影響範囲を絞るためにDBのテーブル設計は一旦スコープから外します。とはいえテーブルがそのままだと処理に響く可能性があるので、見直したい場合はソースコード上でモデル分割を行います。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2266459/c230332a-9751-dd92-451e-b51e244a5b29.png)

### 4. リファクタリング（インターフェースを変更する）

今度はインターフェースの変更を主に検討していきます。インターフェースを固定していたいくつかの主要クラスは解体され、いくつかのクラスに分割されます。よって、今度はそれらの呼び出し元のリファクタリングがメイン作業になる想定です。それら主要クラス内の処理については「3. リファクタリング（インターフェースは変更しない）」の中で綺麗になっており単体テストも記載されているので、比較的低リスクでリファクタリングを行うことが可能となります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2266459/c9c59490-8bd5-429e-c01f-8f473fe960a8.png)

### 5. （テーブル設計の変更）

最後にDBのテーブルの設計の変更を検討していきます。ただし影響範囲が出庫依頼作成処理に限らないので、別プロジェクトとして計画して慎重に行う必要があります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2266459/71c1471d-76d5-fbc6-df75-e6ec15a4d5ec.png)

## 今はどのステップ？

で、今はどのステップかと言うと、「1. 仕様整理」と「3. リファクタリング（インターフェースは変更しない）」を並行して進めているような状態です。
「1. 仕様整理」および「2. 単体テストを充実させる」については私が担当しており、「3. リファクタリング（インターフェースは変更しない）」および「4. リファクタリング（インターフェースを変更する）」についてはより歴の長い他のメンバーが担当しています。
リファクタリングの設計には時間を要しますし、単体テストの実装とリファクタリングの実施はコンフリクトすることがないので並行して進める判断をしています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2266459/9ed1a993-dbee-0719-e2ef-1b282e26378f.png)

## 何が大変で、どのように乗り越えようとしているか

現在私はひたすらコードと向き合って仕様を整理しているわけですが、リファクタリングを必要とするようなコードで、かつ非常に巨大かつ複雑なドメインを扱っているため、仕様自体や運用の理解に苦労したり、歴史的背景の理解に苦労したりとなかなか大変な作業が続いています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2266459/bb83a833-4bc4-6691-2bbb-562e4a18ece8.png)

その中で効果的に仕様整理を進め、モチベーションを維持するためにいくつかの工夫をしています。
まず、主に出庫依頼作成処理との付き合いが長い識者を集めた定例MTGを隔週で行っています。そこでは進捗報告をするだけでなく仕様に関する認識を合わせたり、歴史的な背景や実際の運用についてコメントを貰ったりして、私の知識不足を補填すると共にプロジェクトメンバーの仕様に対する解像度を上げています。
次に、専用のslackチャンネルを立ち上げてそこで不明点や気づいたことを垂れ流しています。こちらではよりスピード感を持って識者から情報をもらったり、また定例MTGには参加していないけれど仕様を知っているメンバーから情報を貰えたりしています。実況中継することでモチベーションの維持にも一役買っています。
最後に、仕様整理から学んだことをエンジニアの週次MTGでの発信しています。知識を自分だけに留めず組織に還元することで少しでもドメインに愛着を持つ開発者を増やしたり、開発力の底上げを図れれば、という狙いです。こちらは現在第6回まで開催済みで「講義の時間だ、単位取れるかな？」や「まるで大河ドラマ」といったような比較的良い反応（？）をもらってモチベーションの維持にも一役買っています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2266459/9213d56e-a127-fbaa-293d-555ee85b7b75.png)

結果的に「1. 仕様整理」に関しては順調に進み8割方完了しました。着手前は未知の処理だった出庫依頼作成処理ですが、（かなりの詳細まで含めて）全体像が掴めた！という感触を持つレベルのところまでやってくることができました。

## 最後に

ということで、巨大な処理のリファクタリングにどのように向き合っているかという話でした。
実際に単体テストを充実させてリファクタリングをリリースしていく作業はこれからですが、負債解消大成功！と言えるように丁寧に進めていきたいです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2266459/38a6481e-acc7-6302-487c-a9519ec969fb.png)