noteのパフォーマンス改善 - サービスの成長によるJSのファイルサイズ増加とN+1クエリ問題に向き合う
noteに投稿された記事数は、累計で昨年同時期比2倍以上に増加。サービスの成長とともに、いままでは問題のなかったコードやクエリにも異変が起きはじめています。そこで、noteでは「パフォーマンス改善週間」という取り組みを行いました。1週間限定で開発チームの有志が横断的に集まり、ページ表示速度やAPIのパフォーマンスなどを改修していく試みです。今回は記事詳細ページに絞って改善が行われました。
パフォーマンス改善プロジェクトの発足はエンジニアが発したSlackの何気ない一言がキッカケとなり、作業開始まで約1ヶ月というスピード感で行われました。最終的には予定よりも多くの改善が行われる結果になったのです。noteが掲げている「すばやく試す」というバリューを体現する取り組みになりました。
最近のGoogle Search Console。このプロジェクト以降も、対応を続けている。
noteとして初めての改善週間はどのような成果があったのか、パフォーマンス改善プロジェクトのみなさんに振り返り座談会を行ってもらいました。
※noteはバックエンドにRuby/Rails、フロントエンドにVue.js/Nuxt.jsを使用している。
エンジニアの有志で始まった、パフォーマンス改善プロジェクトの成り立ち
木村:2019年2月入社。機能チーム リーダーで、今回のプロジェクトのファシリテート役でもある
木村さん:最初にパフォーマンス改善プロジェクトが発足した理由を話していきましょうか。
そもそもnoteが成長するにつれて、ページ表示の速度やAPIのパフォーマンスが落ちてきていることは前々から問題視されていました。パフォーマンスはユーザーのいい体験にも関係する要因の一つです。全クリエイターに多大な影響があるということで、本格的に改善していくことにしました。
橋本さん:パフォーマンス改善は前々から個人的にやりたいと思っていたのですが、それぞれタスクを抱えているため取り組むためのキッカケがありませんでした。Slackでのやりとりはちょうどいい機会でしたね。
深谷さん:入社する前にnoteを見たときは、もう少しページ表示が速かった気がしたんです。2020年の1月くらいだったかな。フロントエンドエンジニアとして速度改善できそうな部分はあると思っていたので、同じくSlackを見て参加したいなと。
木村さん:Slackで「パフォーマンス改善するのはどうだろう」という話題になったときに、我も我もと有志が集まってきてくれたのはうれしかった。
緑川さん:僕の場合はそもそもプロジェクトがあること自体を知らなくて、CTOの今さんに「パフォーマンス改善プロジェクトが立ち上がったけどやってみない?」と聞かれて参加しました。APIのレスポンスタイムとかDBの負荷は興味あったので。
木村さん:有志が集まっていたので、そのあとは経営陣と話し合いをして「まずはパフォーマンス改善を1週間やってみる」ことに決まりました。そこから現状の問題共有と1週間でなにをやるのかを議題にキックオフをやりましたね。覚えてます?
橋本:2020年6月入社。機能チーム所属。得意分野はフロントエンド、JavaScript
橋本さん:覚えてます。1週間しかないということで今回は記事詳細ページだけに絞ろうって決めましたね。
木村さん:そうです、そうです。キックオフをやる前からSlackで「ここに問題あるよ」という意見を出しあっていたので話し合いはスムーズでした。初日は課題感がある箇所をみんなでGoogleフォームにザーッと書いていって担当を割り振っていきましたね。
橋本さん:普段コードを書いている中で「このあたり、やばそうだな」とかは頭にあったので、タスクは、すぐにたくさん出ていた印象があります。
緑川さん:エンジニアチームは普段からパフォーマンスに課題感を持っていたイメージはありますね。だからタスクもだしやすかったのかも。
木村さん:1日目にブレストしてタスクの割り振り、3日目に中間報告、5日目に最終ブレストをやるという形で、2.5日のスプリントみたいな形になりましたね。
緑川さん:月曜の朝10時からいきなりキックオフなのが、時間的に辛かった(笑) でも改善週間の作業を始める前に全員で認識を合わせられたのはよかったですね。
サービスが拡大するとパフォーマンスは劣化する。定期的な改善が必要
木村さん:改善週間でやったことを振り返っていきましょうか。いくつかやったことはあると思うのですが、橋本さんはどの取り組みが印象に残っていますか?
橋本さん:JavaScript のファイルサイズの削減はかなりできましたね。サードパーティのライブラリは読み込まないようにしたりVuexのstoreを整理したりして、メインのJSは半分くらいにしました。
木村さん:フロントエンドエンジニアで協力してやってくれましたね。ありがたい。
橋本さん:サイズは減らせたけど根本的な解決にはなっていない部分も多くあります。例えばモーダルコンポーネントを共通JSから外すことができたのですが、読み込みが発生してしまっているのが現状。本格的に解消するにはモーダルの再設計が必要です。
木村さん:コードの削減は実装に影響ある部分もあって難しいですよね。JSのサイズを減らすときって、どうやって修正箇所のあたりをつけました?
橋本さん:Webpack Bundle Analyzerの結果を見て、処理の割合が多くて解消しやすそうな部分から手を付けていきました。
JSのサイズ削減は早い段階で解決していったのですが、失敗もありました。削減しながら一つずつ速度を計測したわけではないので、どれが効いているのかわからない状態になってしまい。
木村さん:そうですね、計測は不可欠でした。「計測ツールどうしようか、将来的には必要だな……」と思っていたら深谷さんが初日にツールを作ってくれていましたね。
深谷:2020年9月入社。開発カイゼンチーム所属。得意分野はフロントエンド、ツール作成
深谷さん:僕はこの中だと社歴が一番浅いですし、記事詳細ページも触ったことがなかったので、なにか起きても原因がわからないという問題がありました。なので自分用にツールを作って計測しようと。ChromeのLighthouseでチェックしてもよかったのですが、いちいち確認するのも大変なので自分用にPythonで作りました。
木村さん:あのツールって仕組み的にはどんな感じだったんですか?
深谷さん:単純ですよ。LighthouseのコマンドでURLを指定して実行し、出力されるJSONを保存しておき、パースした上でスプレッドシートに出力する感じです。20個くらいのいろいろなタイプの記事を指定して、それぞれ5回くらい計測して平均値をだしました。
木村さん:改善をして逆に遅くなることも考えられるので、改修を戻す判断も必要かなと思っていました。深谷さんのツールを判断基準にすることができたので助かりました。
フロントエンドはおふたりに担当してもらって、バックエンドは緑川さんを中心に見てもらいましたね。
緑川:2018年8月入社。機能チーム所属。得意分野はサーバサイド、Ruby on Rails
緑川さん:まずはAPIやDBを中心になにが遅いのか重点的に調べていきました。APIのログを見たり、どのクエリが遅いのか観察したり。遅い原因さえわかれば解決できることが多いので。原因を見つけたら修正してステージングにあげて確認…をひたすら繰り返しました。サービスへのインパクトが高そうなものから改修していく感じで。
深谷さん:ステージングでの確認はログを見る形ですか?
緑川さん:そうですね。基本的にはレスポンスタイムがどれだけ早くなるのかに注目しました。ページの表示速度が遅くなるのはたいていサーバサイドに原因がありがちですし。
木村さん:サーバサイドは定期的に見ていかないといけませんね。
緑川さん:データ量が増えたことによって、効率的なインデックスを使えてないクエリやN+1問題などが、サービス開始時に比べてかなりAPIのレスポンスタイムに悪影響を及ぼすようになってきました。今回はそういったものを中心に修正していった感じです。
木村さん:フロントは大なり小なり問題があってすぐ解決できるものもありますが、サーバサイドが関わっている部分は複雑なので修正にも時間がかかりますからね。サービスが拡大していくと速度が変わるから定期的に修正しないといけない。
緑川さん:探せばまだまだ修正すべき部分はいっぱいありますね。今回は1週間しかなかったので、大きな箇所に手をだしづらいというのもありましたし。
短期で意味はなくとも長期的に見て効果がある施策を進めていくべき
深谷さんが作成したツールでの計測結果。プロジェクト終了後も改善されていることが分かる。
木村さん:自分以外の改善で印象に残っているものや良かったものってありますか?
橋本さん:逆に良くなかったものがない(笑)
緑川さん:もしかしたら誰か「この改善は許さない」みたいなものがあるのかも(笑)
木村さん:あー、でもこのまま改善を進めていくと、いずれはそういった問題はでてくるかもしれません。改善したことが原因で速度が遅くなる、とか。
緑川さん:個人的には今回フロントの改善が多かったのであまり気になることはありませんでしたね。速度改善の改修を見ていると「それ本当に効果ある?」「オカルトじゃない?」みたいなことってけっこうあるので(笑)
木村さん:運用していく中での改善は継続的にやっていく必要がありますね。今回は1週間まずやってみるというトライだったので、今後は計測ツールもきちんと導入して、効果があったのか判断していきましょう。
橋本さん:あと気になるのは、短期的にみたら意味はないけど長期的にみたら効果が高いものは進めていきたいですね。逆に長期で見たらやばいコードはレビューで潰していきたい。
緑川さん:データサイズが小さければ問題ないコードやクエリは、わりとありますからね。データが増えたときにまずい状態になる。
橋本さん:フロントのJSが肥大化しているのはまさにそれが原因です。「この書き方しているといずれは問題になるよね」がどんどん積み重なった感じ。開発スピードとの兼ね合いもあって難しいけれど、設計から解消できるものもあるのではないでしょうか。
木村さん:その設計の穴にどう気づくのかが難しい問題。noteはフロントもバックエンドも両方やるひとが多いので、新機能を追加したときに負債になりえるコードが紛れてしまうことはありそう。
橋本さん:そうですね。僕の場合はサーバ側が得意ではないので、レビューをしっかりやってもらうとありがたいです。
緑川さん:どちらにせよ問題が起きちゃうときは起きちゃいますからね。リリースした時点からどのくらい速くなったのか遅くなったのか、データが増えていくことによってどれぐらい遅くなっていったのか分からないというのは怖い。
速度を計測する仕組みだったり、それに対応できる体制はつくるべきだと思います。現状だと本番で問題が起きたとしても「このクエリってどこのコードだっけ?」となりやすいですよね。
深谷さん:高負荷環境が欲しいです。ステージングの段階で、負荷を確認したいので。
橋本さん:フロントは深谷さんがつくってくれたツールみたいにデプロイごとに計測していくといいかもしれませんね。
深谷さん:計測を続けていけばデバイス環境ごとにもデータが溜まりますしね。Lighthouseを使っていけばできそうだけど、まだ手を付けられていません。あとはbundleのデータ量とかもデプロイごとに測りたいな。
緑川さん:今はサーバサイドに問題があるとみんなでワーッと原因を見つけて、ワーッと直すみたいな感じですからね。スロークエリを監視するのは当たり前として、APIのレスポンスがだんだんと遅くなっている経緯とかもシステムで監視できるようにしたい。
木村さん:いずれは計測ツールをしっかり導入した上で、みんなが見れるように仕組み化しないと。
緑川さん:不安なままエンジニアが作業していくのは健全ではありませんからね。誰が悪い、ではなく全員が自分の書いたコードは安全だという精神的な保証があればいい。
木村さん:そう考えるとこの1週間を通して短期的な成果もあったけれど、長期的な目的をつくるべきという発見もあったのはよかった。
とりあえず初めてのパフォーマンス改善は終わりましたが、まだまだ修正すべきページは多いですね。というか全ページです(笑)
橋本さん:成果もありましたし、この改善は続けていきたいですね。
木村さん:運用体制ももっと詰めていかないと。さっき話に出ていた計測もそうですし、エンジニアが定期的にリソースを持てるような形にするとか。
橋本さん:パフォーマンスバジェット的なものが決められているといいのかも。新機能つくるときもパフォーマンスの最低ラインを超えないようにすれば基準がわかりやすい。
木村さん:観測をして、指標があって、リソースを決められる社内体制がある、というのが理想的ですね。
緑川さん:体制が整っていないまま進めてしまうと、改善が改悪になることもありますからね。
最後に:パフォーマンス改善週間を終えたそれぞれの感想
深谷さん:パフォーマンスの改善は、ユーザー体験に直結するので、地道に続けていきたいですね。入社してすぐに、こういうプロジェクトに参加できたのはよかった。
緑川さん:こういった取り組みはエンジニアとしても会社としてもすごく良い。APIの修正をしていたら周りのエンジニアがアドバイスをくれたり、N+1問題の修正を個人的にやってくれたひともいた。パフォーマンス改善をやったことで、開発チーム全体にもパフォーマンスに対する意識づけができたのかなと思います。今後どうなるかはわからないけど続けていきたい。
橋本さん:やるぞって決めてから走り出すまでが早かったのはよかった。チームやプロジェクトが違うひとと横断して一緒に仕事ができて、知見をあわせつつissueを解決していくのは楽しいです。短い期間だけど成果もだせたのでやってよかった。
木村さん:有志として集まってチームを組んで、問題に取り組めたのは大きかった。当初の目的よりも成果がでたのは、一人ひとりがスキルを活かしリーダーシップと協力する行動をした結果だと思います。今回は記事詳細ページだけでしたが、いずれは全ページを改善していきクリエイターにより良い環境を提供できるようにしたい。
▼noteを一緒に作りませんか?
Text by megaya