ENECHANGE Developer Blog

ENECHANGE開発者ブログ

案件固有の機能を開発していくためのリポジトリ戦略

EV充電サービス事業部の@yuyasatです。記事を書くのがだいぶ久しぶりになっていました。EV充電サービス事業部は2022年1月に発足した事業部で、マネージャーをしております。今回はEV充電サービス事業部の話ではなく、私が昨年まで在籍していたエネルギークラウド事業部のEMAPというサービスにおける課題およびその対応についてお話しさせていただきます。

背景と課題

EMAPは電力会社・ガス会社向けに特化したいわゆるVertical SaaSです。EMAPのサービス詳細については、EMAPのLPにお任せしますが、本記事では、開発当初から課題であった各案件ごとの対応についてお話させていただきます。

EMAPのクライアントには、古くから私たちに電気やガスといったライフラインを提供してくれている大手電力会社やガス会社がいます。業界ごとにその業界の慣習に合わせた形でサービスを開発しなければいけないように、提供先の会社毎に応じて個別に対応しないといけない箇所はどうしても出てきてしまいます。細かい例で言うとA社では「お申し込み」という文言にしたいが、B社では「お申込み」にしたいといった各社の他のホームページに合わせた文言統一などが挙げられます。

EMAPの中で、電気やガスの情報およびシミュレーションをAPI形式で提供するサービスに関してはどの会社に対しても同じ形式で提供するので、リポジトリやサーバーなども複数社に対して同一のものを使っていますが、電力・ガス料金のシミュレーションや申し込み画面に関しては各社固有に事情によりカスタマイズが施されており、ソースコード資産の管理が課題となっていました。

具体的な課題の一例について記載しようと思います。実は、EMAPが世に出される前は、電気やガスの比較サービスであるエネチェンジの申し込み機能と同じソースコードやインフラで電力会社に提供していた時期がありました。その際は各社のカスタマイズはSTIを用いて表示上の変化やバリデーションや仕様の調整を行っていました。その際の事情に関しては、「エネチェンジでのSTIの使い方紹介」をお読みいただければと思うのですが、やはり同じリポジトリで同じインフラ(サーバーやデータベース)でサービスを提供するとなると、A社向けの反映でリリースしたがB社への挙動や表示に影響が出てしまったと言うことがありました。また、開発時には常に他の会社の仕様に影響がないかということを気にしなければならず開発の時間もかかってしまいますし、開発者がすべての会社の仕様を把握するのは現実的ではありませんから、開発時のストレスも増えるという状態になっていました。

課題への取り組み

そうした時代を経てEMAPを提供する際には、思い切って各社ごとにリポジトリおよびインフラも分けてしまうことにしました。SaaSというサービス提供形態上、できる限り一つのリポジトリ(ソースコード)で各社に提供できるようにすることが理想ではありますが、業界の特性上やむを得ずこのような判断に至ったということになるわけです。(インフラの分離については別の機会に投稿しようと思います)

とはいえ各案件が出てきたごとにゼロからリポジトリを立ち上げコードを書いていくのでは、似たような開発を毎回しなければいけませんし、生産性も経済性も落ちてしまいます。私たちはできるだけ早く開発・リリースを行ってクライアントやその先の電気やガスの利用者に価値を提供したいと思っていますので、そのような開発環境は好ましくありません。

そこで、EMAPのベースの機能をもつリポジトリ(emap-baseリポジトリ)を用意することにしました。新しいクライアント案件が立ち上がったらこのリポジトリから新たにリポジトリを作り、各社ごとにカスタマイズを入れていくという形をとることにしました。こうすれば過去開発したコードを利用して各社向けの開発をすればよく開発期間を短くすることができます。

しかし、これだけでは、emap-baseリポジトリの機能が一向に拡充されず例えばA社の案件で出てきた要望をB社向けの案件でも使いたいと言った場合にまた開発することになってしまいます。emap-baseリポジトリの機能は継続して追加されてEMAPとしての価値も増大させていく必要があるわけです。そこで私たちは各案件が立ち上がって仕様が決まったタイミングや定期的に何をベースとして取り込むべきかというのを話し合い、emap-baseリポジトリへ機能を追加していくようにしています。

リポジトリへの機能追加

次に、emap-baseリポジトリへ機能を追加していくための流れについて説明します。案件の立ち上げフェーズでの新規機能については、emap-baseリポジトリで開発していきます。ベースとなる機能をまずemap-baseリポジトリにcommitを積んで開発していき、ベース機能ができた段階で、別リポジトリに切り出し案件固有の機能を実装していきます。

一方で、要望は案件の立ち上げフェーズだけでなく運用フェーズでの追加開発などでも出てきます。この場合はemap-baseリポジトリへの取り込みには少し工夫が必要です。git管理の場合、多くの場合一つのディレクトリでは一つのリモートリポジトリを参照することが多いと思いますが(submoduleを使う場合を除く)、リモートリポジトリは複数参照することが可能です。リスト1は一例ですが、git remote addしてgit fetchした後にリポジトリ切り出し後のリポジトリのcommitをcherry-pickすることができます。このような形で別のリポジトリのcommitをemap-baseリポジトリへ反映できます。(このとき、developへのPullRequest mergeの際にsquash mergeを利用してcommitを1つにまとめておくと楽だと思います。)

cd emap-base
git remote add emap-provider-a https://github.com/xxxxxxx/emap-provider-a.git
git fetch emap-provider-a
git cherry-pick commithash
リスト1. 別のリポジトリのcommitをcherry-pickするコマンド例

ただし、ベースリポジトリと切り出したリポジトリ(リスト1ではemap-provider-aリポジトリ)で大きな乖離がある場合はうまく取り込めない可能性があります(conflictが起きる)。その場合はPullRequestなどをみて再度実装していく必要があります。この行為は一見して同じ作業を行っており、できれば減らすべき作業と考える方もいるかもしれません。しかし私はむしろ過去の実装のリファクタをするいい機会と捉えています。一度実装したものでも後から見ればこうすればよかったと振り返ることがよくあります。また、運用していく中で別の設計にすればよかったと思うこともあります。 そうした内容をベースリポジトリに取り込む際に反映していくのです。ベースリポジトリは長く育てていく物ですので、より洗練された実装を反映すべきです。その機会があると捉えれば、同じような実装を2回するのも悪くはないと思っています。

まとめ

最後に同一リポジトリで開発していく場合と別リポジトリで開発する場合の特徴を開発・QA(Quality Assuarance)・案件間の移植性・メンテナンス性(バージョンアップ等)という観点で整理したものを表1に示します。
同一リポジトリで別ブランチで運用するというケースも過去にみたことがありますが、その場合はPullRequestのマージ先を間違えたり自分の案件に関係ないブランチが存在して混乱したりするのでその場合は別リポジトリで管理した方が良く今回は省略させていただきました。
各開発現場毎にさまざまな事情があると思いますので最適な管理方法を日々模索していきたいと思います。

表1. 同一リポジトリ管理と別リポジトリ管理の比較
同一リポジトリ 別リポジトリ
開発 ×複数案件の仕様を確実に保持しながら開発する必要があり、それぞれケアしながら開発することで開発速度が低下する ◯案件固有の仕様を優先でき、開発速度が上がる
QA ×ターゲットにしない案件のQAも行う必要がある ◯ターゲットにする案件のQAを行うだけでよい
案件間の移植性 ◯同じリポジトリ内なので容易 △少し手間ではあるがgitのコマンドで可能。conflictが起きる場合も実装を改善する良い機会。
メンテナンス性 ◯同じリポジトリなので1回で済む △RubyやRailsのバージョンアップは各リポジトリで行う必要有。一方で、小規模な案件からバージョンアップすることで不具合の影響範囲を限定的にできるメリットも。