LINE株式会社は、2023年10月1日にLINEヤフー株式会社になりました。LINEヤフー株式会社の新しいブログはこちらです。 LINEヤフー Tech Blog

8年続くPerlプロダクトをKotlinに書き換えた話。システムリプレイスの勘所

2021年11月10日・11日の2日間にわたり、LINEのオンライン技術カンファレンス「LINE DEVELOPER DAY 2021」が開催されました。特別連載企画「 DEVDAY21 +Interview 」では、登壇者たちに発表内容をさらに深堀りし、発表では触れられなかった内容や裏話についてインタビューします。今回の対象セッションは「8年続くPerlプロダクトをKotlinに書き換えた話」です。

「LINEポイント」はLINE Payでの支払いやLINEの各種サービスで貯めたり使ったりできる共通ポイントです。この「LINEポイント」を貯められるさまざまなミッションの提供や、連携サービスでの利用案内をするポータルサービスが「LINEポイントクラブ」です。

「LINEポイントクラブ」は2013年6月に前身のサービスが提供開始されてから2021年まで、Perlで開発されてきました。8年もの長きにわたって多種多様な機能が追加・変更・削除されたことにより、システムが極めて複雑化していました。また、Perlエンジニアの数は世界的に減少傾向にあるため、今後はエンジニアが不足し、開発が続行不可能になることが予測されたのです。

そこで「LINEポイントクラブ」では、2021年1月から1年をかけてプログラムをPerlからKotlinへと書き換えるプロジェクトを遂行しました。このインタビューでは開発4センター Official Account 開発室 OA Dev 3チーム サーバーサイドエンジニアの大原康平が、プロジェクトにおける工夫や学びを解説します。

アーキテクチャ設計において大切にした要件

――OA Dev 3チームの役割や大原さんの業務内容を教えてください。

OA Dev 3チームは「LINEポイントクラブ」のサーバーサイド開発を担当しています。「LINEポイントクラブ」には「貯める」タブと「使う」タブが存在しており、私は「貯める」タブに関する機能の設計・開発や成果物のレビュー、インフラの整備などを担っています。PerlからKotlinへのリプレイスプロジェクトではDev Leadというロールを任されており、前述の業務だけではなくプロジェクトの進捗管理や各種の方針決定なども担いました。

――「LINE DEVELOPER DAY 2021」で語られた事例において、システム設計の際に大切にしたことや前提となった機能要件はあるでしょうか?

最も大切にしたのは、「LINEポイントクラブ」の運営に携わる人々が、施策を円滑に遂行できるようにすることです。近年、「LINEポイントクラブ」はリワード広告というよりもインナーマーケティングの広告メディアという側面が強くなっているため、メディアとしての柔軟性や拡張性を重視する必要がありました。

また、「貯める」タブと「使う」タブのアプリケーションを統合することを目指しました。旧アーキテクチャではこれらのタブが別々のアプリケーションであったため、メンテナンスコストが高くなっていました。機能を追加・修正する場合には両方のアプリケーションに手を入れる必要があり、工数が肥大化していたのです。

加えて、旧アーキテクチャでは処理速度が求められる箇所でトリッキーな設計やプログラムの記法を用いており、可読性が犠牲になっているケースがありました。シンプルかつパフォーマンスの良いシステムにするため、設計自体を抜本的に見直しました。

例えば、「LINEポイントクラブ」のトップページで必要になるデータをRedisに持っておくことで、MySQLへアクセスしない構成に変更した箇所があります。また、ジョブキューとしてKafkaを導入することで、自分たちでメンテナンスしていた独自のジョブキューを廃止しました。それ以外にも、肥大化しやすいデータをMySQL以外のデータベースで持つことにより、管理を容易にした部分もあります。

――Kotlinへの書き換え後のシステムアーキテクチャ構築において、設計案として挙がったものの、選ばなかった案があれば教えてください。

もともと、データベースのスキーマ構成が複雑化していたため、アーキテクチャ変更に合わせてその箇所にも手を入れようと構想していました。しかし、工数が膨大になりすぎることが判明したため、スキーマ変更を最低限に抑え、アプリケーションのコア部分でデータベースの複雑性を隠蔽する方針にしました。

また、今回一部のMySQLのテーブルをHBaseへ移行することも検討しました。しかし、工数が膨らむことを考慮し、このプロジェクト以前にスケーラビリティを改善していた部分ではあったので、HBaseに移行せずMySQLのままにしています。データベースを移行しなくても、求められる性能要件を満たせるためです。

PerlからKotlinへのリプレイスにおける知見と反省点

――セッション内では、PerlからKotlinへ移行するにあたり、すでに使われていない機能を調査し、不要なコードを削除した旨が解説されていました。

この調査はなかなか大変でした。「メソッドが他のコードから呼び出されていないこと」はリポジトリ内のコードを全文検索すればわかるため比較的楽なのですが、「メソッドが他のコードから呼び出されているものの、実際にはその処理を通ることはなく使われていない」ケースもあり、不要コードの洗い出しに苦労したためです。

――どのような方法で不要コードの調査を行いましたか?

メソッドや数行程度のコードに関しては、Kotlinへと移行する際の設計・開発段階で、移行しづらいと感じた機能があればその都度調査をし、削除できるかどうかを検討しました。例えば、使われていないMySQLのテーブルやカラムを洗い出すことで、それに関連するコードを削りました。コードを読めばテーブルやカラムへのクエリ発行が行われていないことを判断できるため、この調査は比較的容易でした。また、APIそのものが呼ばれていないケースに関しては、Nginxのアクセスログを参照することで判断しました。

――大原さんはセッション内で、プロジェクト中盤から終盤にかけていくつかの反省点があったと述べられていました。それらの点をふまえて、もしも同じ性質のプロジェクトを再び担当するならば、改善したい点はありますか?

セッション内では「Kotlinのnullableを多用し過ぎたこと」「同じ名称だが複数の意味を示す変数を一律String型で扱ったこと」「ビジネスロジックが外の世界のことを知ってしまっている」という3つの反省点を述べました。前半の2つは、これまでPerlを書いていたエンジニアが、不慣れなままKotlinを書いたために起きた問題だと考えています。

――より詳しく教えてください。

Kotlinにはnullableとnon-nullという概念が存在します。non-nullはnullを許容しない変数であり、nullableはnullを許容する変数です。Perlにはこれらの概念が存在していません。Perlの変数はKotlinでいうところのnullableのようなものです。そのため、Perlに慣れたエンジニアがKotlinへの書き換える際に、nullableを多用してしまい、コードの可読性とnull安全性が下がってしまう事態が発生しました。

2つ目の変数の反省点についても解説すると、私たちのサービスには複数の意味を持つ「digest」という用語があります。「digest」とはユーザーとキャンペーンの組み合わせごとに発行されるトークンのようなものです。「Limited digest」「Unlimited digest」「LINE digest」「External LINE digest」などの語句があり、それぞれ異なる概念を示しています。

プロジェクトの初期には、これらの情報をすべてString型で扱っていました。その結果、コード内にある「digest」がどの種類の情報を示すのか、非常にわかりづらくなってしまいました。この事態に対処するために、私たちはプロジェクト中盤からKotlinのtypealiasを使い始めました。

チーム内にはKotlinに慣れているエンジニアももちろんいましたから、コードレビューの際にこれらの課題を検出できたケースもありました。しかし、すべてのコードレビューでそれを実現できたわけではありませんでしたね。もしも、今後Kotlinから別の言語に書き換えるケースがあれば、同様の課題は生じると思います。

――どのような方法で課題を解消できるでしょうか?

新しい言語を学ぶ場合、わからないことがあるのは当然ですから、各エンジニアがその都度学習やレビューをし、チーム全体として知見を高めていくしかないでしょうね。また、プロジェクト全体に影響しそうな前提情報などがあれば、早めにチーム内で共有することが大切だと感じています。

――「ビジネスロジックが外の世界のことを知ってしまっている」に関してはいかがでしょうか?

私たちのプロジェクトでは、一部のビジネスロジックがio.grpcパッケージのStatusRuntimeException例外を投げていました。つまり、ビジネスロジックが外の世界との通信方法を知っている状況だったのです。その結果、gRPCを用いていない別のアプリケーションでそのビジネスロジックを使いたかったものの、共通処理として切り出すことが困難という状況が発生しました。

――この失敗から何を学ばれたでしょうか?

「○○のロジックはこのアプリケーションでしか使わないだろう」という思い込みは危険だということでしょうか。プロジェクト初期は、そのビジネスロジックをgRPC以外のアプリケーションでも将来的に使用可能にすることを考えていませんでした。

プロジェクト終盤は時間が足りなくなりリファクタリングに時間を割くことが難しくなってくるため、序盤での方針策定や認識合わせが重要だと痛感しました。

勇気を持ってスケジュール延期することも重要な意思決定

――「LINEポイントクラブ」の事例のように、システムの言語のリプレイスプロジェクトに携わる読者に伝えたいことはありますか?

こうしたプロジェクトでは想定外のことが数多く発生しますし、工数の見積もりも難しいです。いくつか工夫してほしい点をお伝えすると、まずエンジニアだけではなくビジネスサイドのメンバーなど関係者全員が力を合わせてプロジェクトを進める必要があるため、開発業務だけではなく情報連携や協力してもらうための働きかけなどを大切にしてほしいです。

また、作業にかかる工数などを鑑みたうえで、現実的なスケジュールを立てることが望ましいです。そして「○○日までに○○の機能を開発完了していなければ延期する」「○○日までにQAで○○と○○の確認が終わっていなければ延期する」といったかなり具体的な締め切りを設けて、その期日を超過したらスケジュールを延期したほうが良いですね。

延期せずに済む方法を検討することも大切ではありますが、無理をした結果として、システムの品質が下がってしまったら元も子もありません。ユーザーやクライアントに迷惑をかけるよりは、勇気を持ってスケジュール調整をし、品質の高いサービスをリリースすることを大切にしましょう。それが結果として、より良いサービスの提供へとつながると思います。

採用情報

LINE株式会社では一緒に働くエンジニアを募集しています!
今回のインタビューと関連する募集ポジションはこちらです。

サーバーサイドエンジニア / LINE(大規模配信管理システム / Large enterprise OA message delivery system)等