OpenAPI x Rails x Next.js でIVRyを0から作り直した話

2023/03/28に公開

こんにちは、電話自動応答サービスIVRyでエンジニアをしている小瀬です!
https://ivry.jp/

実は先月、ユーザーさんには伝わらない形で大幅な「作り直し」を行っておりました。
余談ですが、このブログを書いている時にちょうどこのツイートがバズっているのを見かけました。

https://twitter.com/MinoDriven/status/1541414589409030144?ref_src=twsrc^tfw|twcamp^tweetembed|twterm^1541414589409030144|twgr^298a14d61209ddcd8ef7831f5e4c248240f2c8b4|twcon^s1_&ref_url=https%3A%2F%2Fnote.com%2Fkose_atsuya%2Fn%2Fn23656bf41413

過去のコードを作り直したいけど、作り直せない、みたいなことってあるあるなんだなと改めて思いました笑

今回僕らは勇気を出して作り直す決断をして、過去のコードはレポジトリ単位で捨てて、新規でAPIを100本弱、2-3ヶ月ほどかけて粛々と実装しました。
完全な作り替えだったので、リスクの高めのリリースでしたが、無事に障害なく乗り越えることができました。

今回は、作り直しの動機や、失敗しないために気をつけたことなどをブログにまとめていきたいと思います。

さらなる飛躍のために、一時停止

IVRyは、初めからリッチな構成で作ったわけではなく、どちらかと言うとプロトタイプを最短で作ることを目的とし、改善の蓄積によって今までアップデートをしてきました。上記のやり方はサービスを立ち上げる時に非常に効率が良く、会社設立当初は、IVRy以外にもさまざまなサービスを実験することができました。

ただIVRyは想像以上の急成長を遂げ、資金面でも3億円の調達が決まり、エンジニアメンバーも増えてきました。

https://prtimes.jp/main/html/rd/p/000000016.000056805.html

このままチームで開発していく基盤としては貧弱だと判断し、思い切ってこのタイミングで過去のコードを捨てる事にしました。

具体的にどう作り替えるのか

今回作り直しの対象になったのは、IVRy のwebページです。電話側のロジックは対象外ですが、設定画面やLPや記事など全てがその対象です。

https://ivry.jp/

以前までのIVRy はRuby on Rails のフレームワークの上でバックエンドとフロントエンドを両方を動かしていました。

これを、バックエンドはRuby on Rails のAPIモードでREST APIだけを提供し、フロントエンドはそのAPIを利用して自由に設計できるように、バックエンドとフロントエンドの切り離しを行いました。

フロントエンドはNext.js + TypeScript の構成で作り直す事にしました。
バックエンドは同じRuby on Railsを使っていますが、レポジトリを新規作成し、0からコードを書く事にしました。

※SST は Server Side Templating の略です
https://qiita.com/kimizuy/items/d33420330479f8c85449#ssr-13

もっと良い設計は存在しないのか?

最近、例えばGraphQLやGo言語などを使った構成を良く見かけるようになりました。こうした動向には今後も常に目を向けていきたいと思っています。
一方で、ベンチャー企業にとって新規開発が止まることは大きな損失です。
できるだけ歩みを止める事なく、大きな発展ができる設計が最も最適な設計だと考えた時に、Ruby on Rails での実装については既に知見が多かったことや、REST APIの場合にフロントとバックエンド共に見通しか良かったため、今のIVRyにとっては最良の設計だと判断しました。

API仕様書が絶対!を担保するために

ここから、開発時に気をつけてきたことを書いていきます。
今回API仕様書がマスターという状態を作ることにこだわりました。極論、仕様書さえあればバックとフロントは会話しなくても良い状態を目指しました。ただ、それを実現するためには仕様書とコードが一致していることを担保する必要があります。今回、バックエンドチームで担保するために工夫したことを書いていきます。

Swagger(Open API) を使う

まず、API仕様書には定番のSwaggerを使いました。

https://www.openapis.org/

Swaggerを使うとフロントエンドはコードの自動生成機能を使えるため、効率よく、かつミスが少ない実装することがます。
ただし、仕様書の重要度はさらに上がる事になります。コードとは異なる仕様書を書いていると、自動生成されたフロントの実装が作り直しになってしまいます。

RSpec のテストをSwagger の仕様書を使って行う

バックエンドのコードと仕様書の担保は、RSpecによって担保する事にしました。

具体的には

  1. response が仕様書と違う場合はテストで落ちるように
  2. request のパラメータが仕様書と違う場合はテストで落ちるように

この二つを実現するためにcommittee-rails というgem を使いました。

https://github.com/willnet/committee-rails

具体的なライブラリや書き方のコツなどは当ブログでは割愛しますが、こちらのOffersさんのブログに詳しく書かれていました。

https://zenn.dev/offers/articles/20220411-open-api-schema

2022/04/11 にこのブログが出た時に、僕らもほぼ同じ方針で開発を進めている最中だったので、このブログを見て間違ってなかったんだなと安心させていただきました。笑
特に、「API 定義していく上で最低限守るべきルール」に書かれている

  • 必須プロパティには required は必ずつける
  • additionalProperties: false もつける

この二つはテストを落とす上でとても大事なパラメータです。
required が無いと、パラメータやレスポンスの不足で落ちなくなりますし、additionalProperties: falseがないと、逆にパラメータやレスポンスの過多で落ちなくなります。
僕らは、基本的に全てにrequired をつけて、任意のものは、nullable: true にする方針で仕様書を書きました。

大規模なリリースで失敗しないために気をつけた事

ポイント①:完璧な復元にはこだわらない

IVRyの機能レベルでのデグレは起きないようにしつつ、細かいデザインの違いや、UI/UXの微妙な挙動の変更などの復元は目指しませんでした。フロントチームには旧IVRyを踏襲しつつ、新しいフレームワークで作りやすいように作ってもらいました。細かい差分のチェックなどを開発時に行わないことで効率的に開発できたことは良かったことかなと思っています。

その結果、ルールの分岐編集など、UI/UXが改善してわかりやすくなったケースもあり、効率化もはかれた上に改善点も多くみられ、利益も大きかったです。

(左が新しいIVRyで右が旧IVRy)

ポイント②:新規機能は入れない

新しい構成にしたついでに、やりたかった新規機能も一緒に入れちゃおうっていう気持ちはもちろんあったのですが、今回はあくまでもIVRyでできることの再現をコンセプトに実装をしました。
そのおかげで、QAとしては旧環境と比較してデグレがないか確認する、というわかりやすいテーマになります。
今回ここで欲張らなかったことは、この大きなリリースで失敗しなかった大きな要因だと思います。

左が新しいIVRyで右が旧IVRy
若干のデザイン変化はあるが大きくは変えてない

ポイント③:DBは設計し直さない

リファクタリングするとDBの設計から見直したくなるものですが、DBの設計は変えず、全く同じDBを使う事にしました。そのメリットは大きく二つありました

  • リリースのスコープが小さくなる
  • 旧環境と新環境を全く同じデータで見比べることができる

特に後者のメリットは大きく、適当なサブドメインを切って新環境をdeployするだけで、全く同じデータを使って旧ページとの比較検証することができました。

「このページ新環境で重いけど、旧環境ではどうなんだっけ?」みたいなことは容易に調べることができます。
コードの差分だけに注力できたことも、リリースが成功した大きな要因だと思います。

ポイント④:テストは基本的に全部書く

全APIのリクエストはもちろんのこと、validation やモデルメソッドなど、基本的に全てのメソッドのRSpecを書きました。
デグレのチェックが簡単にできて、新規開発の時に機能の担保を保証できるので、テストは必須ですね。テストは今後もずっと書いていきます。

ポイント⑤:容易な切り戻し

今回のリリースはDBを共通にしたため、URLの向き先を変えるだけのリリースでした。

そのため、リリースに失敗したとしても向き先を基に戻すだけでDBの操作も必要がなかったため、大きなリリースの割には低リスクで挑めたことが大きかったです。
(結果的に切り戻しは発生しませんでしたが)

成功の裏鍵はCSチームとの良好な関係性

今回、数ヶ月の間新規開発を止める事になりました。その間、当然CSチームの方にはお客様からの要望がくることもありました。

今回本当に良かったなと思ったのが、CSチームとエンジニアチームの関係性が非常に良好なので、お互いに事情を話し合いながら全体最適な方向に持っていくことができました。

リリースの時には、万が一障害が起きたときのために、CSチームが深夜リリースに立ち会ってくれました。

ちなみにオフィスは↑から400㎡のオフィスに移転するので次回はもう少し映える写真を載せます。笑

CSチームの皆様にはいつご迷惑をかけるかわからないので、この関係性がずっと続くように、毎日感謝を伝えたいなと思っています。笑

リリース当日は全員参拝

やるべきことは全てやったため、各人にお願いして神社にいってもらい、参拝してもらいました。

こうして、この大きいリリースは無事成功することができました。
IVRyではこうした文化が根付きつつあり、最近はお願いしなくても自主的に皆さんが神社に参拝するようになってきています。笑

作り直し効果は絶大

過去に作ったものを作り直せず、ずっと使い続けてしまうと言うのは割とあるあるだとおもいます。
今回僕らも勇気を出して作り直す決断をし、3ヶ月弱の間新機能開発をストップさせましたが、予想通りそのメリットは大きいものでした。

プロダクト連携による拡張性の向上

サーバーがAPI化されたことと、メンテナンスされている仕様書ができたことにより、拡張性は遥かに大きくなりました。今後、社内外の様々なサービスとの連携を積極的に取り組んでいこうと思っております。

社内では既にIVRy Media という別で作っていた問い合わせフォーム機能との連携がローンチされました。

機能の詳細はリリースノートに記載されています

https://ivry-jp.notion.site/2022-06-30-eddea82ea85c4d11bd6da1721b40fbe8

アジリティの向上

作り直しがローンチされてから、以前よりも安定的かつ効率的に新規機能の開発を行うことができています。
例えば、6/23にローンチした「音声合成の種類を選択できる機能」は影響範囲が大きい機能だったのですが、テストによってデグレが起きてないことを担保できたので、効率的かつ不具合を出すことなくリリースすることができました。

すでに作り直し期間を巻き返せそうなペースで新機能を開発できています。

知見のリセット

エンジニアメンバーが増えた時期でもあったので、過去のコードを捨てて作り直したことによってメンバー全員がIVRyに詳しい状態を作ることができました。
作り直しのタイミングとして今回最も適切なタイミングだったなと思っています。

IVRyで一緒に開発するエンジニアさん募集してます

今IVRy はエンジニアも増え、400㎡超のオフィスへの移転を予定してます。
本当、今ベンチャー企業の一番面白い時期なんじゃないかと思ってます。
興味ある方は是非ご応募や、カジュアル面談などお待ちしております!
https://youtrust.jp/recruitment_posts/2e309ee5a8536e6df3fa7966940c8071

https://ivry-jp.notion.site/IVRy-e1d47e4a79ba4f9d8a891fc938e02271

IVRyテックブログ

Discussion