😽

Version 1 から Version 2 へのフルリプレイス

に公開

はじめに

はじめまして、Recustomer でエンジニアをやっている rikuto です!

私はプロダクトのリリース前から在籍しており、今回は プロダクトのVersion 1からVersion 2へのフルリプレイス を通して経験した、現場のリアルな苦悩や気づきを共有したいと思います。
(どちらかというとお気持ち表明が中心です)

どんなプロダクトなのか気になる方は下記のサービスサイトを見ていただければと思います。
https://recustomer.me/

フルリプレイスに至った経緯

約1年という長い期間をかけてフルリプレイスを行いました。
なぜそこまでしてリプレイスが必要だったのか? 大きな理由は以下の 3点 です。

  1. 要望に応えられない仕様
  2. 1行のなんでもない変更に神経を使うコード
  3. 外部サービスへの依存

1. 特定の要望に特化しすぎた「機能の寄せ集め」

当時のプロダクトはSaaSであるにも関わらず、特定の顧客要望に特化した機能の継ぎ足しで作られていました。
その結果、既存機能を拡張して新しい要望に応える仕様を作ることすら ほぼ不可能 な状態に陥っていました。

2. 恐怖の「2000行スーパー関数」

ソースコードにおいては、「様々なユースケースにおけるコードが全て共通化されている」というアンチパターンを踏んでいました。

その象徴が、1つの関数で2000行を超えるスーパー関数です。
この関数はプロダクトのコア機能の全てを担っており、この関数が全てを解決すると言っても過言ではなかったです。
ネストは10を超えていたような記憶があります...
そのため、新しいタスクの見積もりをする際、エンジニアが一番最初に行う確認作業はこれでした。

「今回の改修は、あの関数を触る必要があるか...?」

おまけに、型(Type)はありません。
まさに、爆発物を処理する時に「赤の線を切るか、青の線を切るか」というような緊張感です。

もちろん不具合を出すわけにはいきません。デプロイ前には毎回大規模な手動テストを実施する必要があり、そのコストの高さからデプロイ頻度は 2週間に1回 程度に留まっていました。

さらに UI のままに BE のコードが設計されており、DB までもが UI をそのまま反映したような設計になっていました。
全くもって別ドメインのデータが同一テーブルに保存されていることにより、オーバーフェッチ問題やDBの拡張性がなくなっていました。

3. Shopifyとの密結合

弊社のプロダクトを、外部システムである Shopify のサードパーティアプリとして、リリースしたこともあり、 Shopify に完全に依存した設計になっていました。
全てのファイルに Shopify という単語があるレベルで依存しており、Shopify 以外のカートシステムとの連携を行うというのが不可能な状態でした。

フルリプレイスをしてどうなったか

抽象化による「変更容易性」の獲得

V1では、1社導入してもらうたびに毎回何らかの機能を実装する必要が多々ありました。しかし、V2では機能を適切に 抽象化 したことで、個別の実装が劇的に減りました。

例えば、「x の時に y をして、紐付く z を作成する」という要望があったとします。

  • V1の場合:
    特定の要望に DB レベルで依存している。
    → 変更容易性が損なわれ、別の要望に対応できない。
     (コードレベルであれば書き換えるだけで問題ないが、外部リソースとも密接に結合していた)
  • V2の場合:
    x(条件)は共通だが、y, zは顧客ごとに変わる可能性がある。
    → x, y を動的に設定可能な仕様・設計 にすることで、様々なケースをカバー。

これについては「少しやりすぎた感」もありました。「要望が温まる(具体的になる)までは作らない」という選択肢が、実は最善だったかもしれないと感じる部分もあります。
機能は作った時点で、将来の拡張性を妨げる可能性を持つということを頭に入れておく必要があるなと実感させられました。

ソフトウェアを疎結合にするための設計プラクティスの適用

ソフトウェアの疎結合化を進め、まだ改善途中ではありますが、V1の最大の問題であった「適切ではない依存性」を解決することができました。
これによって、適切な工数の見積もりが可能になりました。これはビジネス判断に大きな影響を与えるものであり、とても良い改善だったなと思っております。

上記に伴いドメインも適切な単位で分割され、UI 都合の設計からスケール可能な設計へと進化しました。

外部連携においても層を隔てることで、弊社が提供するコアな部分の外部サービス依存を切り離せました。
昨今のアプリケーションでは、外部サービスの連携が当たり前になっている中で、内部でコントロール不可能な外部サービスへの依存はとても危険だなということを実感させられました。

結果:デプロイが「嫌なもの」から「楽しいもの」へ

依存関係が整理され、テストが書きやすくなったことで、安心して高頻度にデプロイが可能になりました。

  • 導入社数の増加:機能都合での障壁がなくなり、導入社数が一気に増えた。
  • 対象顧客の拡大:外部連携の拡張容易性に伴い、対象を広げらた。
  • 顧客満足度の向上:要望へのレスポンスが早くなった。
  • エンジニアのメンタル向上:デプロイのストレスからの解放。
  • 攻めの開発へ:リソースを新規機能開発へ注げるように。

「デプロイが嫌なものから楽しいものになった」 というのが 一番の実感です。 昔は正直、デプロイのマージをするのがストレスでした(笑)。

学んだこと

長々と書きましたが、今回のリプレイスを通して学んだ結論は以下の2点です。

1. 仕様について

要望が少なく、ドメインの深い部分が見えていない時点では、「機能が本当に必要になるまで作らない」 という選択を取ることも重要だなと思いました。
複数の要望が集まり、深い知見が得られた段階ではじめて、共通項を見出し 抽象化された機能 を作ることが大切だと学びました。

2. コードの共通化について

「過剰なコードの共通化」を避け、高凝集・疎結合であることの重要性を失敗を持って学びました。
分けることによる複雑さは「足し算的」にしか増えませんが、共通化による複雑さは「掛け算的」に膨れ上がり、人間が把握できる量をすぐに超えてしまうということを実感しました。


最後まで読んでいただきありがとうございました!
弊社の他のメンバーもアドベントカレンダーで記事を毎日投稿しているので、興味ある方は是非!!
https://qiita.com/advent-calendar/2025/recustomer

Discussion