⚒️

Ubie は Go と Node.js の会社になります

2022/12/14に公開
1

Ubie では、創業当初から Server-Side Kotlin を推進してきましたが、全社的な技術選定を再度行い、これからは Go と Node.js を中心とすることにしました。

本記事では、Go と Node.js を選定した理由や、それを普及させる取り組み、そして選定の流れを紹介します。

経緯

これまで Ubie では技術スタックを発散させてきていて、現在は Kotlin、Go、Node.js、Ruby、Python のバックエンドサービスが動いています。以前は新規開発が多く、それぞれに携わるメンバーが技術選定をすることにより、最大瞬間風速を出せるなどのメリットがありました。しかし、現在では弊害が目立ってきています。

まず、事業成長に伴って運用の重要性が増しています。人材が潤沢とは言えないスタートアップにおいて、様々な技術スタックを安定運用することはコストが高すぎると感じています。ベストプラクティスの整備や開発支援の仕組みづくりのコストも高いです。
また、「プロダクト開発エンジニア」としてフロントエンド、バックエンドの技術領域を問わない形での新規採用を進める中で、その人材流動性も阻害してしまっています。

そのため、これからは技術スタックを収束させていくこととし、その選定を行いました。

これからの技術スタック

プロダクトサービスでは Node.js、基盤サービスでは Go を採用することとしました。ただしこれはあくまで原則で、合理的理由がある場合には他の言語も採用します。例えば、機械学習をやるサービスには Python を使うなどが考えられます。

フロントエンドに面するサービス[3]には GraphQL、サービス間通信には gRPC を採用します。


アーキテクチャのイメージ

Why Node.js?

すでに大半のエンジニアがフロントエンド開発で TypeScript を書いていて、全社にインストールしやすいです。中長期的にもフロントエンド開発で TypeScript を使い続ける可能性は非常に高く、仮に将来的に Node.js を減らしていくことになっても、TypeScript 自体は浮かずに済みます。

また、GraphQL はファーストクラスのサポートがあり、gRPC も及第点です。gRPC に関しては protobuf-es なども出てきていて、今後の発展にも期待できます。

運用観点では、フロントエンドで Next.js を採用しているので、どちらにせよ SSR サーバーの Node.js を運用する必要があり、運用対象のランタイムが増えないことが魅力でした。また、NestJS が優秀で、メトリクス周りなどはサクッと実装することができました。

Node.js と言っていますが、将来的には Deno をはじめとする他のランタイムに載せ替える可能性もあります。そう単純ではないでしょうが、WinterCG によって互換性を持つ取り組みも行われています。より良いランタイムへ載せ替えやすいというのもありつつ、そもそも複数のランタイムが、協調して仕様策定しながらも競っているエコシステムの健全性も評価しています。

Why Go?

言語設計的に書き手によるブレが少なく、可読性が高いです。これはフロントエンドをやってきたエンジニアがバックエンドも触る Ubie の環境と相性が良いです。

運用観点では、やはり圧倒的な起動速度とメモリ効率によるスケーラビリティが魅力です。特に Ubie では toC, toB のプロダクトをそれぞれ展開しています。toC のスパイクによって toB と共通の基盤サービスが落ちたりしないよう、スケーラビリティを重視しました。

Why NOT Kotlin?

言語機能は過不足なく、Java の資産が活かせるメリットも大きいと感じています。自分含め、社内でも Kotlin を書いていて楽しいエンジニアが多いです。

一方で、ビルドの遅さや、開発ツールチェイン周りに辛さを感じています。
IntelliJ IDEA に内蔵された静的解析やフォーマッタの出来が良いのですが、実行するためには IDEA が丸々必要なので、CI で実行するにはコストが高いです。Qodana という製品もありますが、まだまだドキュメントも薄く発展途上と考えています。

そして最近の Kotlin のリリースを見ると、Kotlin/JS や Kotlin/Native に開発リソースの多くを割いていて、我々のペインはなかなか解決されないように見えます。これは批判ではなく、Ubie の求める方向性との違いだと考えています。

また、我々には JVM の運用が難しいと感じています。JVM の運用知見を持ちつつ、Ubie の人材要件にマッチする候補者はかなりレアで、採用できていません。実際に、我々が十分にチューニングできていないために、スケーラビリティやパフォーマンスが問題になることが何度かありました。

学習しつつ進めていくことを考えると、前述の通りどちらにせよ SSR サーバーとして運用せざるを得ない Node.js に寄せるほうが楽でした。

Why GraphQL?

フロントエンドにおいては、Apollo Client の高度なキャッシュの仕組みが魅力でした。特に Ubie のサービスは一問一答形式であり、ミューテーションが多い性質であるため、正規化されたキャッシュは UX 上のメリットが大きいです。
そしてコード生成の品質が高く、スキーマ設計さえちゃんとしていれば、大きなコストをかけずに上述のキャッシュを実現できます。

また、toC サービスでは Web 版の他にモバイルアプリも提供しています。現在は Capacitor を用いて単一のコードベースで実装していますが、将来的な分離を考えると、クライアント側で柔軟にクエリできることも魅力でした。

しかしサーバー側の実装に関しては、DataLoader 周りなどが比較的大変だと感じています。そのため、サーバー間通信には GraphQL を採用しないこととしました。

Why gRPC?

Ubie が扱う医療ドメインは非常に複雑かつ広範です。それに起因して、複数のサービスが扱うドメイン領域が被ってしまったり、不整合が発生してしまったりという課題がありました。

この問題を解決するために、全社横断でドメインの整理をしたく、1リポジトリで proto を管理する googleapis/googleapis のようなアプローチと相性が良い gRPC を選定しました。

Go と Node.js においては、Buf によって静的解析やコード生成のエコシステムが整いつつあるのも魅力でした。まだまだ発展途上ではありますが、基盤がモダンに整っているため、以下のように必要なものはサクッと自作できています。

https://github.com/yukukotani/protoc-gen-nestjs

既存サービスのリプレイスはしない

Server-Side Kotlin などで書かれている既存サービスを、この技術選定の文脈でリプレイスすることは今のところ考えていません。

ただし、多くの既存サービスはドメインたくさん抱えすぎ問題があったり、色々とレガシーだったりして、徐々に別サービスに切り出していこうと考えています。それに合わせて、新たな技術スタックへ移行する予定です。

シン・技術スタックを普及させるための取り組み

サービステンプレート

Node.js、Go それぞれで新規サービスをすぐに立ち上げられるテンプレートリポジトリを用意しています。このテンプレートには今のところ以下が含まれ、今後も拡充する予定です。

  • アプリケーションの雛形
  • データベースアクセス
  • ロギング
  • 基本的なメトリクス
  • 静的解析
  • CI/CD ワークフロー

雛形は単純な echo サーバーではなく、ビジネスロジックを持つアプリケーション[4]を実装しておくことで、より具体的に実装イメージをつかめるようにしています。

言語ごとのガイドライン

Go や Node.js の経験がないエンジニアもすぐに立ち上がるよう、言語ごとに詳細なガイドラインを用意しています。これは近いうちに体裁を整えて外部公開したいと考えています。


ガイドラインをチラ見せ

ブートストラップ支援

サービステンプレートやガイドラインは用意していますが、もちろんそれが完全にワークするとは考えていなく、利用事例からフィードバックループを回して改善する必要があります。その改善のリードタイムをできる限り短くするため、当面は新規サービスの立ち上げに技術戦略チームが入り、支援しつつテンプレートへの還元を行います。

技術選定の進め方

基本的には、バックエンド技術戦略というロールを持っている自分が雛形を作り、エンジニアにレビューをもらう形で進めました。

まずはエンジニア全員で集まり、現状の課題点やありうる選択肢を発散させる会をやりました。 そこで出た選択肢を対象に、「得意かどうか」「好きかどうか」などのアンケートを取りました。ここではあえて「書いていて楽しい」のようにエモーショナルな部分も集計しました。

そしてアンケートを参考に、技術選定のドラフトを作ってレビューを募集しました。このドラフトには、選定の理由をかなり詳細に書くことで、より腹落ちしてもらえたと思いますし、自分が見えていない論点の指摘を受けることもできました。

レビューを全て打ち返して残論点を潰し終わった後、確定版をアナウンスし、テンプレートリポジトリやガイドラインの整備を行いました。僕は Go にあまり詳しくないので、ここは @m_mizutani が協力してくれました。

要所で合意を取りつつ、基本は個人で意思決定することでスピーディーに進められたかなと思います。

おわりに

Ubie がなぜ Go と Node.js の会社になるのか、どのように決めたのかを紹介しました。

まだまだやりたいことはたくさんあります。

  • Node.js / Go 共に最強の設計を模索する
    • その言語に詳しくないエンジニアも最小学習コストでうまく書ける設計がチャレンジングでおもしろい
  • ESLintプラグインとか作りつつ静的解析を最強にする
  • ガイドラインの充実化
  • テンプレート・ガイドラインの公開

開発・運用共にこれから推進していく一番おもしろいタイミングなので、一緒にやってくれる方を大募集しています。

https://recruit.ubie.life/jd_dev/eng_prod

https://recruit.ubie.life/jd_dev/eng_sre

https://recruit.ubie.life/jd_dev/platform

脚注
  1. ざっくり言うと粒度の荒いマイクロサービス。https://thenewstack.io/miniservices-a-realistic-alternative-to-microservices/ ↩︎

  2. 例えば認証認可など ↩︎

  3. 現在はプロダクトサービスがBFF的機能も担っていて、これを分離するかは議論中です ↩︎

  4. ELIZA という素朴なチャットボットを実装しています ↩︎

Ubie テックブログ

Discussion

tommy34tommy34

言語ごとのガイドラインはどこかで公開されているでしょうか。