こんにちは、Sansan Engineering Unitで副部長を務めている笹川 裕人です。Sansan技術本部のテックリードたちが取り組んでいる技術的な取り組みやチャレンジを対談形式で深掘りする「Sansan Tech Talk」。今回は第2回として、Bill One Engineering Unitのチーフアーキテクトを務める加藤 耕太にインタビューしました。
▼連載記事はこちら
buildersbox.corp-sansan.com
笹川:それでは早速ですが、加藤さんの自己紹介をお願いします。
加藤:インボイス管理サービス「Bill One」のチーフアーキテクトをしている加藤 耕太です。Sansan株式会社に入社してから約5年が経ちます。その間の4年間、Bill Oneの立ち上げに携わってきました。プロジェクト初期はわずか2〜3人での開発でしたが、現在はチームも70人ほどに拡大しました。入社前はSIerで働いており、趣味のプログラミングに関するブログを書いていたことがきっかけで、書籍を出版する機会もありました。
笹川:よろしくおねがいします。早速ですが、直近で取り組みたい技術的な課題について教えてください。
加藤:最近の取り組みとして、Bill Oneで新たなプラットフォームチームを立ち上げています。この取り組みの主な目的は、開発者の体験と生産性を向上させることです。Bill Oneはマイクロサービス・アーキテクチャを採用していますが、マイクロサービスの肥大化が課題になっています。特に、初期に構築されたマイクロサービスであるInvoiceサービスは現在も重要な機能を果たしていますが、サービスの規模が拡大しているため開発が難しくなっています。そのため、サービスを分割するなどして、よりコアな機能の改善を進めやすくしたいと考えています。
笹川:なるほど、マイクロサービスとコンウェイの法則に関しては、組織構造がどう影響するかという議論はよくありますね。そのInvoiceサービスには、ひとつの大きな組織だけが関わっているのでしょうか、それとも複数のチームが関与していますか?
加藤:複数のチームが関わっています。そのため、チーム間で連携を取りながら開発する必要がありますが、将来的には1サービスは1つのチームが担当する体制に移行したいと考えています。
笹川:サービスを分割するのは一般的には手間がかかりますよね。サービス間の依存関係が深くて大変な部分がありそうですが、分割の境界が見えていて比較的容易に分けられそうな部分はありますか?
加藤:分割できそうな部分はありますが、正直なところ簡単ではありません。ただ、その分割できそうな部分の機能が他のサービスにも転用できそうという見込みがあるので、このタイミングでサービス分割と共に取り組みたいと思います。
笹川:つまり、機能を汎用化して他のサービスでも利用することで、開発の工数を削減できるわけですね。
加藤:はい、その通りです。
笹川:ちなみに、その大きなサービスをどれくらいの小さな単位に分割できそうですか?
加藤:分割しすぎると開発が複雑になる恐れがあるので、3つのサービスに分けて、それぞれのチームが1つのサービスを担当する体制が良いと考えています。
笹川:聞いている限り現状の課題としては、リリース時の調整が必要な点が考えられますが、他にも何かありますか?
加藤:ビルドや起動、テストの時間が長くなってしまうことで、開発の生産性が低下している部分があります。
現状、別々のマイクロサービスであれば、複数チームが同時にリリースできるので、適切にサービスを分割できれば、全体の生産性も向上します。
笹川:元々1つのサービスを分割した場合、サービス同士は相互に影響がある可能性もあり、分割後のリリース時の品質保持や動作確認が特に重要になりますね。そのための対策について、何か考えはありますか?
加藤:はい、分割した機能が他のサービスでも使用されることを考え、まずは他のサービスでその機能を使い、問題がないことを確認しながら徐々に本番環境に導入することで、リスクを減らせると考えています。
笹川:話を聞いていると、現在の成長が健全なものだなと感じます。以前は見えにくかった課題が、サービスの成長に伴って明らかになってきていて、それらの課題に適切に対応を進められるようになっていますね。これは、ポジティブなサイクルだと思います。
最近読んだ記事にも、マイクロサービスのコア部が肥大化する問題は常に存在するとありました。これは避けられない課題かもしれませんね。他にも抱えている大きな課題はありますか?
加藤:請求書を検索する機能のリクエスト数が多く、システムへの負荷が高い状態です。この請求書検索のパフォーマンス改善も課題のひとつです。
笹川:検索機能に必要な要件には、どのようなものがありますか?
加藤:請求日や請求金額などが主な検索要件です。
笹川:金額に基づく検索が頻繁に行われるんですね。そうすると、金額と請求日は検索で重要な要素となりますね。
加藤:そうですね。他にも多くの検索項目があり、請求書の詳細検索を開くと、画面が埋まるほどの項目が表示されます。
笹川:そんなに項目があるんですね。請求書自体が保持するデータの項目が多いと、データベースでのジョインも多くなり、表示するまでに時間がかかることがありそうですね。
加藤:かなりジョインしていますね。
笹川:なるほど。となると検索自体は一つひとつマッチするデータを引っ張ってくればよさそうですが、請求書の表示はパフォーマンスが低下してしまう部分もあるんですかね。
加藤:1ページあたりの件数は少ないため、表示はそこまで問題ないと思いますが、ちゃんと検索にマッチするデータを取得するためには、複雑なクエリの実行計画を練る必要があるので、パフォーマンスチューニングは難儀しています。
笹川:更には、検索は精度が求められそうですね。検索機能が充実していなければ、大規模な企業では業務をスムーズに行えないでしょうし。
加藤:そうですね。請求書のデータは毎月蓄積され、長期間にわたって保存されるため、検索性能の向上は必須ですね。
Bill Oneの利用が増えるにつれて、パフォーマンスの問題が浮上してくるため改善しようとしています。ジョインの多さが一因となっており、テーブルの正規化と専用の検索テーブルを作ることで、この問題を解決できるのではと考えて取り組みを進めています。
笹川:ジョイン済みの広いテーブルに対して検索できるようにするイメージですね。この取り組みは非常に興味深いです。バックエンドで非同期に処理を行ったり、データの書き込み時に必要な計算を実行したりする方法や、個人的にはあまり好みではないですがデータベースのトリガーを使うこともひとつの手かもしれません。
加藤:悩ましいですよね。トリガーを使う場合、存在を忘れられがちですからね。
笹川:マイグレーション時に問題を引き起こす可能性がありますからね。覚え続けるのは難しいので、コードで表現できると良さそうですよね。
加藤:マテリアライズドビューを使用して、元データが更新されるたびに即時に反映できれば比較的容易に実装できるかもしれませんが、私たちが使用しているPostgreSQLではマテリアライズドビューの差分更新はサポートされていません。
笹川:ないですよね。この問題はまさに直近で解決したいものですか?
加藤:はい、現在取り組んでいて、年内に対応を完了したいです。
笹川:それは素晴らしいですね。将来的には他にどのような課題に取り組みたいと思っていますか?
加藤: 将来的にはグローバル展開が非常に魅力的だと感じています。現在、タイやシンガポールで当社のサービスが利用されていますが、すべて日本のリージョンを使用しています。今は問題なく機能していますが、今後複数のリージョンを使用する規模になると、新たなチャレンジに取り組めて面白そうだなと思っています。
笹川:複数のリージョンでの展開を進める際には、どのような課題が予想されますか?
加藤:世界中のユーザーに快適なサービスを提供するためには、例えば請求者のデータをユーザーに近いリージョンに配置する必要があります。一方で、グローバルで1つのログイン画面が使えると便利なので、ユーザーを管理するマイクロサービスは別のリージョンに配置するかもしれません。また、EUのように特定のデータ保護規制がある地域では別のアプローチが必要になるかもしれません。そのため、データの配置の理解を深めておく必要がありそうです。
笹川:確かに、国によっては独自の厳しい規制があったりしますね。私も以前、グローバルサービスのGDPR(General Data Protection Regulationの略。個人データ保護やその取り扱いについて詳細に定められたEU域内の各国に適用される法令)対応に携わりましたが、あれはなかなか苦労しました。加藤さんが今後取り組む際は頑張ってください。私は横でニヤニヤしながら見ていますね(笑)
加藤:(笑)確かに、グローバルな規制対応は複雑で難しい課題ですよね。
笹川:国によってはGDPRに加えてさらに厳しい国内法があるため、対応が本当に大変です。高額な罰金が科される可能性があるため、保守的な判断をせざるを得ないこともあります。現地のアドバイザーと協議しながら進める必要がありますし、そこでの判断がビジネスに与える影響は大きいです。ただ、それも含めてグローバルで事業を展開する際のチャレンジなのかもしれません。
グローバル展開はただ多言語対応をするだけでなく、各国の規制やデータの配置、プライバシーの問題、さらには各国の商習慣に合わせた機能の開発など、多岐にわたる課題がありますよね。
加藤:そうですね。実際にタイ向けに作っている特有の機能があります。タイでは西暦に加えて仏暦も使用されるため、システムで日付を読み取る際に西暦か仏暦かを適切に判断する必要がありました。
笹川:そういう文化的な違いに対応するのは面白そうですが、同時に複雑さも増しますね。間違った順序でデータを処理してしまうと、思わぬトラブルにつながる可能性があります。
加藤:そうなんです。一カ国だけでも多くの課題がありますが、さまざまな国に展開していくと、それぞれに適した対応が求められるため、多様な課題に直面することがありますが、それがまた新たな発見や成長につながると思っています。
笹川:グローバル化はSansanにとって重要ですし、面白い課題が多いですね。他にも取り組んでいることはありますか?
加藤:近い将来の視点で言えば、フィリピンに設立されたSansan Global Development Center, Inc.(以下、SGDC)があります。SGDCでもエンジニアを採用し、Bill Oneの開発にも協力してもらっています。SGDCのチームとの連携を強化し、Bill Oneの開発をさらに加速させることも課題のひとつです。
笹川:なるほど、私もSGDCの採用プロセスに関わっていますが、レベルの高い人材が増えているのを感じます。その影響でチームの規模も拡大していますね。東南アジア市場向けの開発が加速していきそうだなと思っています。また、プロダクトマネジメントの観点から見ても、非常に刺激的な展開になりそうですね。
加藤:ローカライズは単純に翻訳するだけではないですよね。例えば、現地で特有の機能を開発する際には、他のチームが管理するマイクロサービスに手を加える必要が出てくることがあります。これは理想的なマイクロサービスの形ではないかもしれませんが、グローバル展開の現実として、しばしば直面する問題です。そのため、できるだけトラブルを避けられるように配慮しています。
笹川:そこは悩ましいですね、国内でも同様のケースがあると思いますが、時差や言語の違いが絡むとさらに複雑になりますよね。それでも、これらの課題を解決していかないと進まないのかなという気はしますけど。
加藤:確かにそうですね。例えば、別チームが管理するマイクロサービスの改修が必要な場合、関連するチームと密に連携し、一緒にペアプロを行いながらお互いに認識を合わせて改善していくイメージですかね。
笹川:ラフにやるなら、プルリクを出してレビュー とマージ、リリースを依頼する方法もありますが、設計思想の食い違いは起きてしまいますよね。チーム間での設計思想の共有をスムーズに行えれば、理想的な状態に近づけそうですが容易ではないですよね。
多くの大手企業がグローバルなチームで協力していますが、ShopifyやSpotify、Netflixなどは自分の担当サービス以外の変更も要求しながら進めていて、先行事例があるはずなので調べてみたいですね。
少し話が変わりますが、国内でも英語を公用語とするグローバルチームが存在しますが、これも協調に役立っていますか?今後のプロダクトのグローバル化に向けて、グローバルチームの存在が生きてくるんですかね?
加藤:どうでしょうか。先ほどのセブの課題を、国内のグローバルチームでも抱えていた時期もありましたが、最近はグローバルチームが一体となってBill Oneビジネスカードや、その他の新規プロジェクトに取り組んでいます。また、そのグローバルチームが担当のマイクロサービスを持っているというあるべき姿に近い状態になっていることからも、チームとしての成熟度が向上していると感じています。
あとは、セブのチームと密に連携し、Bill Oneの開発スタイルや文化をしっかり伝えていく役割も担ってもらっています。
笹川:開発スタイルや文化の共有は、開発プロセスにおいて非常に重要ですよね。例えば、セブに限らず新しく入ってくる人のオンボーディングにも大きな影響を与えると思います。オンボーディングの仕組みやLinterのスタイル設定が1つになるところも含めて重要だと思います。開発スタイルや文化の共有に当たって、Bill Oneでは具体的にどのような取り組みをしていますか?
加藤:基本的なことですが、開発ルールをLinterやFormatterで統一していて、ドキュメントは、国内のグローバルチームの尽力もあって日英両方用意されています。
また、具体的なものを挙げると、データベーススキーマでテーブルを設計する際、問題が起こらないように自動テストを利用し、特定の形式のテーブル作成を禁止するルールを設定しています。
笹川:それは面白いですね。どのように制御しているんですか?
加藤:私たちはPostgreSQLを使用していますが、スキーマ情報を取得できるテーブルやビューがあるので、それを利用してスキーマのチェックを行っています。
笹川:information_schemaを使用しているわけですね。具体的にどのようなテーブルが制限されているのでしょうか?
加藤:例えば、マルチテナント環境でテナント間のデータ分離を実現するために、PostgreSQLの行レベルセキュリティという機能を利用しています。また、外部キーの設定にはOn delete cascadeを使っているかを確認し、全テーブルが適切に設定されているかをチェックしています。
笹川:その対策はテナントがサービスを解約した際の対応ですね。
データの消去を忘れることなく、関連する全てのデータが一緒に削除されるようにしているんですね。
加藤:その通りです。ただし、消去処理の際にはインデックスが不足しているとパフォーマンスに影響が出るので、適切にインデックスを設定するようにしています。
笹川:なるほど、ちなみに私はアプリケーションサイドで対応するのが好みです。外部キーの話は度々論争になりがちですが、設計時にしっかりとルールを設定しておくことで、後々の管理が楽になるというメリットは大きいと思います。
適切な設定になっているのかは、機械的にチェックできるようにしているんですか?
加藤:そうですね。昔はテストを書いていましたが、スキーマの変更を自動でチェックするために、schemalintというツールにコントリビュートし、機能を拡張して導入しました。これは通常のLinterのように動作し、私たちのデータベース設計ルールに沿ったコードが書かれているかを自動で検証してくれます。
笹川:それは素晴らしいですね。自分でツールを改善し、必要な機能を追加することは、技術的な成長にもつながりますし、全体の作業効率を向上させられると思います。
加藤:確かにそうですね。私は個人的にもOSSの開発に興味があり、Kotlinのコードをリファクタリングするツールを作成しています。
笹川:それは非常に興味深いですね。そのリファクタリングは、どのようなレベルで対応していますか?文字列レベルでのリプレイスを簡単にできるのか、セマンティクスまで確認できるようにASTを見てそれを保持しながら書き換えられるのか。
加藤:後者のほうですね。ASTとしてパースして書き換えられるktcodeshift*1というツールを作っています。 jscodeshiftというJavaScript向けのツールがありますが、それのKotlin版のようなイメージです。Bill Oneの開発で扱う大量のKotlinコードに対して、大規模な変更を一括で効率的に適用可能です。これにより、手作業での時間を大幅に削減し、リファクタリングの躊躇をなくせます。
笹川:それはいいですね。事前に検証されていれば安全に使用できそうですね。
加藤:基本的には全部テストコードがあるので、テストが通ればリファクタリングして大丈夫かなと思っています。
笹川:素晴らしいですね。リファクタリングツールは、DSLのようなものの指示を基に書き換えを行うんですか?それとも、決まりきったルールを基に変換するイメージですか?
加藤:Kotlin Scriptを使用して変換指示を書くと、スクリプトに従って変換処理が実行され、変換後のASTをソースコードを書き戻します。
笹川:つまり、Bill Oneに対する大規模な変更も、このツールを通じて効率的に、かつ安全に適用できるわけですね。
加藤:そうですね。このツールにより、大規模な改善が必要な場合にも迅速かつ確実に対応できるので、開発チーム全体の生産性の向上に寄与しています。
笹川:このツールによって、全体のリポジトリに同じルールを適用することができるので、一貫性が保たれますね。マージされた変更をすぐにリリースする流れも、自動化された環境であればスムーズに進行できそうです。面白そうなので、あとで実装を見てみます。そんなにジェネリックに書けるんですね。
加藤:はい、スクリプトはジェネリックに書けますが、冗長になりがちなので、もう少し構文を工夫することで使いやすさを向上させたいと考えています。
笹川:どこまで使いやすくするかというバランスがあるような気がしていますが、使い勝手が多少悪くても、安全性が高く、正確に機能する方が結局はメリットが大きい気がします。まだ具体的な中身を見ていないので何とも言えませんが(笑)
技術で面倒な問題を解決するのは素晴らしいことです。これからのさらなる発展を期待しています!
加藤:ありがとうございます。
笹川:ありがとうございました!
*1:Kotlinのソースコード変換に興味があればぜひ使ってみてください!