14年目のサービスが経験したリファクタリングの課題

西原優人氏(以下、西原):「14年目のサービスと今後も歩むためのリファクタリング戦略」と題しまして、株式会社ラクスの西原が発表させていただきます。よろしくお願いします。

まず自己紹介です。西原優人と申します。Twitterをやっているので、よかったらフォローしてください。あまりつぶやいてはないですが(笑)。

経歴ですが、2015年に新卒でラクスに入社しまして、「メールディーラー」や「チャットディーラー」といったサービスの開発を経験し、現在は「配配メール」の開発を担当しています。

よく触る技術としましては、PHPやJavaScript、以前まではNode.jsも多く触っていました。趣味はフットサルやスノーボードで、身体を動かすことが好きです。ゲームもやったりします。(スライドの「アウトドア」「インドア」を指して)両方ともにペンギンと入っているのは、ペンギンを見に行ったり、画像を集めたりするのが趣味です。

本題に入っていきます。まず今回、リファクタリングをしたサービスの「配配メール」について、簡単に説明していきます。「配配メール」とは、メールマーケティングに必要な機能を備えたメール配信サービスで、メルマガの配信ツールなどを想像していただいたらよいかと思います。

マーケティングオートメーションツールを備えたプランもあります。メルマガをただ配信するだけではなくて、そのメルマガを受け取った人が開封したり、メールの中に含まれているURLをクリックしたりしたアクションをトリガーとして、メールをさらに送ったり、お客さんがどういった状態なのかをこちらで管理できるサービスになっています。

長く続くサービスの課題

現在、サービス開始から14年目となっております。長く続くサービスの課題として、ありがちな負債が多々ありました。複雑化したコードや書式が古いコードなどがありました。ただ、これらに手を出したいのですが、なかなか出すことができませんでした。

そういった状態が続いていくことでどんどん月日が過ぎていった状態です(笑)。現在動いているコードに手を入れることのリスクや、新機能の開発ではなく、直接利益にはならない心理的抵抗感など、いろいろな要因があった感じです。

ただ、この状態をずっと続けていくと秘伝のタレのように継ぎ足されていく過程で負債も日々増加していきます。イケてないコードが増えることで、さらに手を出しづらくなり、もうどうしようもない状態でした。

課題に向き合うきっかけとしまして、チーム状況の変化がありました。チームメンバーの増加がありまして、ここ3年間で6名から11名へほぼ倍増しました。僕もこの中で異動してきたメンバーのうちの1人です。1バージョンでの開発工数も、人数増加に伴い増えまして、11人月から約23人月と、こちらも倍ほどに増えました。

こういった経緯からコードに手を入れる人や機会が増えていきました。このまま課題を放置すると、これまで以上の速度で負債がどんどん増加していってしまう問題があったので、どうにかしたいと思いました。「そうだ、リファクタリングしよう」ということで、動き出しました。

まず、どういったものをリファクタリングしていくのか、3つの軸を基準に考えていきました。1つ目が今後の開発で手を入れる可能性が高いところですね。最近の配配メールではフロントエンド周辺などに手を入れることが多かったので、まずはフロントエンド周辺に対して手を入れていこうと考えました。

次に、今明確に負債になっていて、開発メンバーの多くが負債だなと感じているコードを対象にしています。最後に、将来的にモダンな開発環境を目指しました。今後も長くサービスを続けていくと開発環境などがどんどん移行していく可能性があるので、そちらにも目を向けていきたいなと思い、そういった観点でも見ています。

リファクタリング対象の決定

そういった観点で見た中で、リファクタリング対象が決定しました。(スライドを指して)こちらのコード、実際にあったコードの部分を真似て僕がサンプルコードを書きました。PHPのコードの中に文字列のJSが組み込まれているコードが多々ありました。

こちらの何が問題かと言うと、PHPとJSが入り乱れていてわかりづらく、そもそもJSが文字列で書かれているのでシンタックスハイライトなどが効かない。もちろん参照先に飛ぶなんてこともできない。最終的なJSがどういったかたちになっているのかというイメージが掴みづらい問題などがありました。

これをコードレビューするときには、「ちょっと見たくないな」という気持ちで毎度取り組んでいる状態でした。

これらをどういったふうにリファクタリングするかと言うと、JS部分はJSファイルにちゃんと分離するために、書き出すかたちを取ろうとしています。スライド左の図から右の図になるようなイメージです。こうなると、見やすくなりました。

このリファクタリングを行ったあとに得られる効果は何かを説明していきたいと思います。可読性の悪いコードが要因となる以下のリスクを低減するので、調査の効率低下や、実装の効率低下が改善されます。レビューでの見落としも、見やすくなることで減るかなと考えています。

IDEの機能を利用可能になることで効率もアップするかと思います。先ほどできないと言っていたことができるようになるのは大きなメリットと考えています。

先ほどリファクタリング対象を決定したと言いましたが、その対象が膨大でした。対象となるJSの関数は2,500個以上はありました。楽観的に見積もったとしても、工数はだいたい50人月以上になったので、けっこう大きな工数という状態です。

なので、もう少し実施できるところまで量を減らすために、フロントエンドの中でも最も利用されているPHPファイルの中に埋め込まれている関数に限定しました。その結果、220個程度まで絞り込めたので、こちらに対してリファクタリングを実施していこうと活動していきました。

リファクタリングへの3つの懸念点

いざリファクタリングを実施する時に、3つほど懸念点がありました。1つ目が対象数です。減らしたといえども、220個の関数をリファクタリングするので、それぞれに開発工程ごとのコストがかかってくるとなると、けっこうコストがかかりそうだと思いました。

2つ目は、工数が大きいことです。追加のタスクや、緊急度の高いタスクなどが来た時に影響が出ないかという不安がありました。

3つ目が、それなりに工数のかかるリファクタリングを行うことへの不安感ですね。最初に申したとおり抵抗感などが少しありました。

それぞれに対して工夫をこらしてリファクタリングの作業を実施していったので、その説明をしていきます。まず1つ目、先ほどの1つ目の懸念点の、対象数が多いので開発工程ごとにコストがかかりそうな可能性に関しては、リファクタリング対象をパターン分けすることで対応しました。

パターンごとに対応方針の検討と設計を実施し、それぞれのパターンごとに工数を見積もるなど行いました。そしてオフショアへ発注することもあるんですが、パターンごとの設計書とサンプルを提供して引き継ぎを行いました。パターン分けしたことで、設計の一部や準備のコストをカットすることができました。

2つ目の、工数が大きく追加タスクがあった場合に影響が出そうという点に関しては、柔軟性の高いタスクとして準備することで対応しました。

まず、複数バージョンで実施する前提で計画を立て、一つひとつを3時間から4時間程度のタスクに分割して用意しました。そうして、緊急度の高いタスクが追加された際には、3、4時間程度のタスクの何個分を削るのか、わかりやすく想定できるようになりました。

オフショアで急な空きのリソースが出ることもあります。そういった際にタスク何個分を追加で向こうに依頼する行動が取れるようにしようと思い、こういうふうに分割をしました。これらを行ったことでリリースへの影響が少なく、安定した開発を見込みました。

3つ目の工夫です。工数のかかるリファクタリングを行うことへの不安感なんですけれども。これに関しては、開発チームとビジネスサイドチームに実施イメージを説明して、チーム全体でどういったふうにするのかというイメージの共有を行いました。

まず開発チームに向けては、現状のままでは計画どおりの開発が困難になっていきますよという課題の共有をしたうえで、リファクタリングのサンプルや複数バージョン先までの計画を用いた実行イメージを共有しました。

ビジネスサイドチームに対しては、開発チームが現在抱えている課題を伝えて、納得感を持ってもらいました。そのうえでリリースには影響を与えないという計画を共有して、安心感を持ってもらいました。

これらを通してチーム全体で納得感や安心感を得て、リファクタリングの実施が動き出しました。

リファクタリングを実施中の現在の状態

現在の状態なんですけど、分割したリファクタリングのうち第1弾のリリースの直前です。ちょうど第1弾の開発終盤に優先タスク、緊急タスクが追加されました。ですが、リファクタリングタスクを減らすことで、無理のない開発を行えました。これは工夫2で行った柔軟性の高いタスクを用意する効果が発揮されたのかなと思います。

そして、ちょうど第2弾の作業中です。第2弾の作業中にオフショアに空きが出た状態が発生したんですが、追加でリファクタリングを発注して対応しました。これによって工夫1で行ったパターン分けや、工夫2で行った柔軟性の高いタスクとして準備するという、これらの工夫の効果が発揮されました。

今後の展望です。現在進行中のリファクタリングをまず完遂することが一番の目的です。次に今回絞り込みで漏れたものとか、そもそも規模が大きくて諦めていたリファクタリングを順次実施していければいいかなと思っています。

3つ目は副次的効果なんですけれども、オフショアチームのスケジュールに余裕が生まれた時の追加依頼ストックとして用意しておくと、すごく精神的に安心感があってよかったので、今後も続けていきたいなと思っています。

最後が、JSを分離したことでTypeScriptへのハードルがかなり下がったかなと思っているので、導入を検討したいなと考えています。

最後にまとめです。長年に渡って手を出すことができなかったリファクタリングの第一歩を踏み出せたと思います。リリースサイクルやチーム状態を意識した工夫で、今のところ順調に進捗しています。今回の事例をもって今後のリファクタリングのモデルケースにできたのかなと思っています。

「そうだ、みんなでリファクタリングしよう」ということで、今後もチーム全体としてリファクタリングの活動をしていきたいなと思っています。以上です。

藤澤貴之氏(以下、藤澤):ありがとうございました。

池田智裕氏(以下、池田):ありがとうございます。

パターン分けの判断基準は?

藤澤:リファクタリングネタですね。ちなみに私も西原が発表してくれた「配配メール」の開発にかつて携わっていたんですけど。リファクタリングはしたいんだけど、いつ負債を返すのか、どうやってやるのかが、なかなかやり切れないところがありました。今は継続的にリファクタリングができるようになったんですね。

西原:そうですね。

藤澤:みなさま発表中にもツイートとかZoomのチャットとか書いていただいていましたが、今からでも大丈夫ですのでご質問等ある方いらっしゃいましたら、ぜひ書いていただければと思います。じゃあ池田さん、みなさまのリアクションを拾っていきましょうか。

池田:そうですね。Zoomのチャットのほうで、おそらく「パターンとはどういうものなのか」という質問なんだと思うんですけれども。

西原:今回の場合ですと……。

藤澤:西原さん、スライドも一緒に見せてもらっていいですか? パターンについて話しているあたりから。

西原:たぶんここを見るのがわかりやすくなるかなと思います。今回の場合、JSの関数を移植することになったので、JSの関数の中でもとくに分岐のない関数もあれば、中にPHPの定数をもとに分岐されているとか、PHP内で変数を使って分岐しているとか。いくつかの種類に分けられました。それらのパターンを、今回は3パターンくらいに分類して設計などを行いました。

藤澤:まずは機械的にというか、定型的に進められるところから進めたと。

西原:最初はルールを見つけるというか、設計しやすいルールはあるのかに着目してパターン分けを行っていきました。

藤澤:そうなんですよね。なかなかどこから手をつけたらみたいなところがあるので。

池田:ほぼ答えが決まっている定型的なものを何パターンか分けていったんですね。

西原:はい。

テストコードはたくさん用意していたか?

池田:同じ方からの質問ですが、「十分なテストコードはリファクタリング前からありましたでしょうか?」ですね。

西原:これに関しては、十分なテストコードを最初に用意していたわけではなくて、普段の開発と同じように、操作して、触ったソースに影響する範囲のテストをしてもらいました。

「配配メール」では自動テストも取り組み始めてはいますが、今回対象としたところにはそういった仕組みはまだなかったので、通常のテストを行いました。

池田:みなさんはリファクタリングビフォーアフターの品質のところがけっこう気になっているようですね。Twitterからの質問ですと、「PHPに埋め込まれたJavaScriptのコードをJSにファイル書き出しする際に、可読性や保守性の観点でリファクタリングも実施しましたか?」「PHP側だけではなく、JSのほうもどうですか?」というようなことですね。

西原:まずは基本的にはそのままいったん移行してほしいと設計書には記載しているんですけれども。すぐ手直しできるようなものであれば、レビューの際にこちらから指摘を出したり、自分で修正する時に直したりはしています。

藤澤:まずは本格的にリファクタリングしていく土台の部分を詰めていっているフェーズという感じですかね?

西原:そうですね。

池田:「ビジネスサイドからの抵抗はなかったですか?」(笑)。

藤澤:このへんはけっこう興味ある方が多いんじゃないですか? ビジネスサイドからの抵抗。

西原:これに関しては、実際僕がこの件で動き始めた時に、あるのかなとけっこうビビっていた部分ではあったんですけれども。とくにそういうことはなく、説明したら「あ、なるほど」と受け入れてもらえたので。そこに障害などは一切なく順調に進められました。

藤澤:なるほど。わかりました。資料の途中にもありましたけど、(ビジネスサイドに)説明をしたんですよね。これやらないとプロダクトの開発速度が遅くなると。

池田:途中の説明でもありましたけど、オフショアに出す部分で案件が滞ってしまう部分があるところでも理解はしていただけたんじゃないかと思います。その分、オフショアの手が空くなら時間の無駄になってしまうところも、もしかしたら理解は得られる部分だったのかもしれないですね。

西原:そうですね。

PHPのバージョンアップには追随できている?

藤澤:じゃあ池田さん、あともう1つくらい拾って、次行きましょうか。

池田:あとはけっこう感想的なところが……。最後に、「PHPのバージョンアップには追随できていますか?」。

西原:はい、PHPはバージョンを上げるたびに、そのバージョンに沿ったかたちでコードの修正は入れていっています。

池田:バージョンアップの時にコードの改変が必要であれば、それはその時にやっているということですよね。

西原:はい、そうですね。

池田:以上ですね。ありがとうございます。

藤澤:その他リアクションで言うと、「段階的にでも改善していくのは大事ですね」って共感いただいている方もいらっしゃいますね。「こんなコード見たら発狂しちゃう」みたいな(笑)。

池田:発狂します。

藤澤:そうですね(笑)。これ以上発狂しないためにリファクタリングを始めたんですよね。「リファクタリング、機会と範囲の見極めがやっぱり難しい」。これ悩ましいですよね。

池田:そうですね~。

藤澤:最適解みたいなものがなかなか難しいですからね。まあまあ、ちょっとずつでもやっていきましょうということで。そうしたら西原の発表は以上とさせていただきます。西原さん、ありがとうございました。

池田:ありがとうございました。

西原:ありがとうございました。