こんにちは、ギフティでエンジニアをやっている中屋(@nakaryo79)です!
はじめに
ギフティではプロダクトのほとんどが Ruby on Rails を使って書かれています。 RubyKaigi のスポンサーや Gotanda.rb の会場提供も行っており、Ruby 界隈では弊社のことを知っていただいてる方も増えてきたのではないでしょうか。 それと同時に、Ruby の会社だよね、というイメージを持たれている方も少なくないかもしれません。
しかし、弊社ではビッグモノリスで一つのシステムを開発しているわけではなく、事業やプロダクトごとにそれぞれシステムを分けて構築しています。 その9割以上は確かに Ruby を使って作られていますが、新しいシステムを立ち上げる際に Ruby だけでなく、最近では Go を採用することもあります。
この記事では弊社がなぜ、どのように Go を使い始めたのかの歴史について、会社としての備忘録も兼ねて書きたいと思います。
経緯
2019年
当時、自分と先輩エンジニアの二人でとあるクーポン管理システムの開発を担当していました。 そのプロダクトは弊社の歴史の中でもかなり古い時代からあり、創業初期のごく限られたリソースの中で作られてきたもので、全体的にレガシーになっていました。 Ruby on Rails で作られた複数のアプリケーションが一つの DB を共有しているという分散モノリスのような構成になっており、コードや機能の責務もスパゲティ状態でした。 そのため、何かしらのエンハンスをするたびに影響範囲調査のためにシステム全体をチェックしないといけなく、また、モデリング上の制約も多く、欲しい機能がそもそも実現不可能ということも少なからず起こっていました。 そこで、モデリングを大きく見直し、適切な粒度でサービスを分割し、作り直そうというシステムリニューアルの話が上がりました。
その際に、先輩エンジニアが技術選定にあたっており、以下の点からバックエンドの言語に Go を使うことになりました。
- サービス分割に際して、通信の効率性とスキーマのバージョン管理の観点からサービス間通信に gRPC を使いたい
- コンテナに乗せやすくするためシングルバイナリにビルドできる
gRPC が公式サポートしている言語のなかで、簡単に実行バイナリがビルドできてモダンなものというと必然的にいくつかに絞られます。
導入にあたってチーム内に(もっというと社内に) Go の知見が全くなかったので、A Tour of Go をやるところから始め、Go の書籍をだいぶ読み漁ったりなどしながら、手探りでコードを書いていました。 しかし、このリニューアルプロジェクトは諸々の事情があって凍結され、この時作っていたものは結局ローンチされずにお蔵入りになってしまいました。
自分が Go を触ったのはこの時が初めてで、Go のバージョンで言うと 1.13 が出るかどうかぐらいの頃だったと思います。Go Modules がちょうど移行しつつある時期で、GOPATH がなんだ、dep がなんだと悪戦苦闘しつつ、後からモジュールモードを知り、GOPATH なんていらなかったんや...となるなどしていました。
2021年
時は流れ、2021年。 上述のプロダクトで再度リニューアルプロジェクトが立ち上がります。
前述したプロジェクトは既存のシステムを根本から大幅にドラスティックに変えていくという大掛かりなものでしたが、今度は特にペインの強い一部の機能に絞ってモデリングを再整理するという、小さく始めてみようという形でスタートしました。
この時、最初にターゲットとしたのが、メールの大量配信機能の見直しです。 事前に設定した予約時刻に、クーポンを一括生成し、そのクーポン URL を数百万単位のユーザーにメールで一斉送信するというものです。
既存の構成は Ruby で書かれたバッチがあり、そこでメールファイルを作り、メール配信サービス側にファイルを転送するという作りになっていました。 当時使っていたメール SaaS 配信サービスの契約プラン的に柔軟にスケールできず、そこがボトルネックになっていたので、思い切って AWS SES に乗り換えようとなりました。 それに合わせて、バッチの言語も実行性能がよく、並行処理を書きやすい Go を使うことにしました。 また、メール配信には予約送信の機能もあり、データベースが必要だったので、そのデータを管理するための API サービスも合わせて Go で開発しました。
この開発時も、チーム内に Go を書いたことがあるのが自分しかいなかったため、最初はかなり大変ではありましたが、今度は無事リリースすることができました。 このサービスインで、弊社で初めて Go をプロダクションで稼働した実績を作れました。
ちなみに、この時に Go を選択した理由は以下です。
- backend API のみで画面を作る予定がなかったので、Rails のようなフルスタックウェブフレームワークを使う必要がなかった
- Rails を使わないなら Ruby を使う必然性はないし、静的型付けかつコンテナに乗せやすい言語を使いたかった
- メールの一括送信の性能要件が結構シビアで、ロジック側である程度並行処理をして速度を出したかった
- メール送信に SES を使う予定で、直列に叩くと速度が出ないことが予想された
- Ruby は並行実行も並列実行も弱い一方、Go はそこが得意分野である
- マシン単位で水平スケールするという手もあるが以下の理由でやめた
- エラーハンドリングや、途中で処理をやめるみたいなのが構成上難しくなるし、考えることが多くなるのであまりやりたくなかった
- SES の API レートリミットを超えないようにマシンを跨いだスケーリングを管理するのが面倒臭い
並行処理などの面での理由はありつつも、Go でないとダメな理由というのもなかったので、慣れている Ruby を使うという選択肢もなくはありませんでした。ただ、自分が Go を書きたかったのと、それを使う理由を説明できたこともあって、まあやってみるかとなりました。この決定が本当に良かったのかどうかはわかりませんが、無事リリースができたことは良かったかなと思います。
2022年〜2024年
2022年の終わり頃から、社内のギフト発行を集約する共通基盤を作るプロジェクトが立ち上がりました。
自分もそのプロジェクトに参画し、ゼロから技術選定をすることになりました。 チームメンバーは自分を含めて3人いましたが、ガッツリ Go を書いたことがある人が他にもいたため、いくつか検討の上、Go を採用しました。
言語選定にあたって ADR を書いたのですが、その Reason 欄をそのまま記載してみます。
- 本決定に関して、特に強い理由はない(Go でないとダメな理由はない)
- 以下、Go を選定したそれらしい理由を記載する
- 社内に慣れているメンバーがおり、ある程度の知見がある
- 今回使う予定の AWS Lambda やコンテナなどとの相性が良い
- 静的型付け言語であるため、堅牢なコードが書ける
- 実行パフォーマンスに優れる
- マシンリソース効率が高い
- クロスコンパイル可能で、OS に依存しないバイナリで実行できる
- 並行処理が書きやすい
書いてある通り、Go でないとダメな理由もなかったので、ここでもまた、全員が一番慣れている Ruby で書くという選択肢もありました。
ただ、UI がなく API だけだったので、ここでも Rails のようなフルスタックウェブフレームワークを使う必然性はありませんでしたし、慣れているものばかり選択していては会社の技術スタックが広がっていかないという懸念もありました。Go と Ruby は割と思想の異なる言語であり、異なる言語パラダイムを学ぶことはエンジニアとしてのスキルにより多くの示唆を与えてくれます。また、全員 Ruby を書くのに多少飽きてきていた、というのもありました笑(飽きはモチベーションに強く関わってきますからね)。
このプロジェクトは、Go に DDD やレイヤードアーキテクチャのエッセンスを取り入れ、順調に歩みを進めました。
2023年には、前職で Go を書いていて、ギフティ入社後もそのまま Go による開発に入ってもらうというような、Go 人材の採用事例もできました。 2024年現在ではチームの人員が5人に増え、開発途中のものも含めてサービス数も3つ、4つと増えてきましたが、一部を除いて大部分は Go を使用しています。
ここ2年ほどの定量的な実績で言うと
- Go の本番投入サービス開発経験人数: 1人 → 5人(業務委託の方を含めると7人)
- Go の本番投入サービス数: 1個 → 3個
- さらに新規開発中サービス数: 2個
こうしてみると、社内で徐々に Go が広がりつつあると言って差し支えないでしょう。
また、2024年には Go Conference にスポンサーとして協賛したりなどし、着実に実績を積みつつあります。
Go を採用してみての所感
Go を採用してきた経緯と現状を説明したところで、じゃあ採用してみて個人的に実際どう感じたのか、を以下にまとめたいと思います(あくまでも個人的な感想です)。
- そんなに外した選択でもなかったというか、むしろ知見が得られて良かった
- Go 自体は最近流行っているし、コミュニティ活動も活発に行われていて、今後も Go 自体の発展に期待できる & 世の中に知見が集積しつつある
- Stack Overflow の survey や、Findy の調査でも Go は高い人気を誇っており、国内外でよりポピュラーになり続けている
- キャッチアップコストが低く、学習しやすい言語ではあった
- 学習曲線が特別ゆるいというわけではない、というのは学習すればするほどわかってくるが、コードが難解すぎて読めない、ということはなかった
- 良くも悪くも黒魔術的コードが少ないので、初心者でも OSS のコードが結構読めるし、ライブラリの挙動で怪しいところがあれば自分でコードを追ったりしている
- コードの書き方に迷ったら標準ライブラリを覗いてみて、参考にするということをよくやる
- これは Ruby を書いているときにはなかった感覚
- 書いてビルドしてコンテナに乗せるまでが楽
- 環境構築なんかで詰まったりすることもほとんどない
- 会社の技術スタックの幅を広げるという意味でも価値があったと思う
- すごく極端な話、プログラミング言語ってそんなにシビアな性能が求められない限り正直どれ選んでもいいというか、選択する際の支配的な理由がない領域だとは思う
- それ故に慣れ親しんでいる言語をどうしても選びがちではあるが、複数の言語を覚えることで幅が広がるというのは人だけでなく組織にも当てはまると思う
- 実際、Ruby とは違うタイプの言語を使うことで Ruby をアンラーニングできている面もある
- Ruby のあの機能がすごい便利なことに気づく
- Go はフレームワークが育ってないので基本自分たちでコーディングアーキテクチャを決めることが多いが、それによって Rails ってやっぱりすごいんだなということに改めて気づく
- Go を使ってますというのが面接応募者の弊社へのちょっとしたアピールにもなる
- 現場側に技術選定の裁量があることや、ちゃんとモダンな言語を取り入れる余地があるということを客観的に伝えられる
- 社内にナレッジがないとやはりキャッチアップが難しい問題はある
- 独学でやってはいるけど、有識者のレビュー受けたことがないので本当にこれでいいんだっけ?みたいなのはやっぱりある
- Go はそんなにブレ幅がない言語なのでまだマシではあるが、、
- 書籍読み漁って勉強会やカンファレンスに参加して情報収集してはいるけど流石にちょっと限界あるよね、みたいな
- 独学でやってはいるけど、有識者のレビュー受けたことがないので本当にこれでいいんだっけ?みたいなのはやっぱりある
- 新しいものを採用するからにはそれを広めていく覚悟みたいなものも求められる
- 自分しか書けないと組織内での持続可能性がなくて詰んでしまう
- 仕事としてやっているので、長期的に保守運用していける状態にしなければならない
- チーム内外に布教していく活動も大事
これから
プログラミング言語は単なるツールです。しかし、システム開発において強力なツールであることに違いありません。
ギフティでは技術選定に関して、現場のエンジニアにかなりの裁量が与えられています。自己組織的 というエンジニアバリューにあるように、エンジニアがしっかりと担当するプロダクトに向き合い、そのプロダクトに最適なテクノロジーを使うのが良いという考え方を重視しているからです。
もちろん、技術スタックがバラけることにより発生するリスクやコストもありますし、無闇やたらに新技術を導入して保守運用できないものを作り上げてしまっては長期的な価値に繋がりません。そうならないよう責任を持った判断が求められ、採用したのであれば保守、布教していく覚悟を持つということも合わせてやっていく必要があります。
自分も Go の書籍が出れば目を通しますし、カンファレンスや勉強会などに参加して積極的に情報収集しています。チーム内で Effective Go などの輪読会をやってみたり、社内のエンジニアを対象に初心者向けの勉強会をやったりと布教活動もしています。採用したからには責任を持って社内に知見を育てていかないとな、と勝手に思っています。
最後に
自分は Go という言語が大好きです。現状、社内では自分のチームしか Go を使用していませんが、他のチームでもチャレンジしたいチームがあれば後押しやサポートをやっていけるといいなと思っています。
また、ギフティには Go を書く環境があります。Ruby だけの会社ではありません。Go を書きたいと思っているかつ、ギフティの事業が気になっているなという方、是非カジュアルにお話ししましょう!