freeeプロジェクト管理でJavaScriptバンドルサイズ削減に取り組んだ話

こんにちは! 中部オフィスでエンジニアをやっているichienです。

5月に入社してfreeeプロジェクト管理の開発を担当していました。
今回はfreeeプロジェクト管理のJS(JavaScript)バンドルサイズを削減した話を紹介します。

改善前はバンドルしたJSファイルが5MB以上に肥大化しておりダウンロードに数秒かかっていました。 その分、ファーストビューの表示もその分遅くなり、ユーザ体験に課題がありました。

今回は次の3つの施策を実施して、5MB1.7MBまで削減し、ダウンロード時間も70%短縮できた話をします。

  • gzip圧縮の適応
  • webpack-bundle-analyzerで現状の可視化と削減対象の決定
  • 不要な日付・祝日データの排除

また、今回の取り組みは一度にすべて対応したわけではありません。次の様にstep-by-stepで進めることを意識しました。

  1. 現状を可視化し理解する
  2. 着目点を決める
  3. 小さな対応を多くやる

では、次から実施内容を紹介していきます!

JSファイルのgzip圧縮

巨大バンドルサイズの原因を探るため、Chrome DevToolsのNetworkタブを確認しました。 バンドルファイルのレスポンスヘッダーにcontent-encoding:gzip がなかったことが、問題特定の決め手になりました。

freeeプロジェクト管理はRuby on Rails+NGINXの構成で稼働しており、JSファイルなどのコンテンツも配布しています。

Railsはデフォルトでコンテンツの圧縮は行わないが、NGINXのngx_http_gzip_moduleを利用することでgzip圧縮を適応することができます。
プロダクトのコードでは、既に次の形で設定がありました。

  • 修正前
# gzip圧縮を有効化
gzip on;  
# gzip圧縮する対象のコンテンツタイプを指定
gzip_types application/javascript text/javascript;  

解決策は漏れていた設定追加だった

解決策はgzip_typesapplication/x-javascriptを追加しただけです。

NGINXのモジュールで利用しているpassengerがJSファイルをapplication/x-javascriptのコンテンツタイプで配信していました。 これに対する設定漏れが原因でした。

修正後のnginx.conf.erbは以下となります。

  • 修正後
gzip on;  
# application/x-javascriptを追加
gzip_types application/x-javascript application/javascript text/javascript;  

原因特定の流れ

原因特定のため、私が辿っていったコードの流れを紹介しておきます。

  • nginx.config.erbのhttpブロックでpassengerのhttp.erbの読み込みを確認。
http {
  <%= include_passenger_internal_template('http.erb', 2) %>
  • http.erb2行目mime.typesの読み込みを確認。
include '<%= PhusionPassenger.resources_dir %>/mime.types';
  • 最後に、mime.types8行目でjsファイルとcontent-typeの対応を確認。
types {
    application/x-javascript  js;

webpack-bundle-analyzerで現状の可視化と削減対象の決定

次に、更に削減可能な箇所を特定するためwebpack-bundle-analyzerを使ってバンドルされているモジュールサイズの可視化を行いました。

webpack bundle analyzerの結果
webpack bundle analyzerの結果

今回はファイルサイズが巨大なJSONファイルに着目し、以下のライブラリのファイル削減方法を紹介します。

date-holidaysの祝日情報を日本のみに限定

freeeプロジェクト管理では、日付ごとに工数入力できる機能があります。 その日の祝日判定にdate-holidaysを使っています。

その中で447KBもあるholidays.jsonを削減する方法を調査しました。

holidays.jsonのファイルサイズ
holidays.jsonのファイルサイズ

調べてみると、このholidays.jsonは世界の祝日情報を持っていました。 プロダクトの要件として日本の祝日情報が判定できれば良いので、日本だけに限定できれば大幅削減可能と考えられます。

ライブラリ側で対象国を絞り込めるholidays2jsonが用意されています。

これをインストール時にnpm post scriptで実行する形を取りました。 これでnpm installyarn install完了後に自動で実行され、データが削減された状態になります。

"scripts": {
    "postinstall": "holidays2json --pick JP --min"
}

対応前後のholidays.json単体で比較すると、444kb44kb となり大幅に小さくできました。

日本に限定したholidays.jsonのファイルサイズ
日本に限定したholidays.jsonのファイルサイズ

moment-timezoneも日本のデータに絞る

moment-timezoneに含まれるlatest.jsonのファイルサイズも186KBとかなり大きいです。

latest.jsonのファイルサイズ
latest.jsonのファイルサイズ

こちらも日本の情報に絞ることでファイルサイズを削減します。

対象国を絞るには、moment-timezone-data-webpack-pluginが利用できます。
webpackの設定例は以下となります。

const MomentTimezoneDataPlugin = require('moment-timezone-data-webpack-plugin');
module.exports = {
    plugins: [
        new MomentTimezoneDataPlugin({
            matchCountries: 'JP'
        }),
    ],
};

結果、moment-timezone自体のデータサイズが186KB17KBとなり、約1/10に削減できました。

日本に限定したmoment-timezoneのファイルサイズ
日本に限定したmoment-timezoneのファイルサイズ

まとめ

紹介した3つの方法で、バンドルサイズを5MB1.7MBまで削減でき、ファイルダウンロード時間も70%短縮できました。

lighthouseのPerformanceスコアを2859まで改善することができました。

改善前 改善後
改善前のlighthouseのPerformanceスコア 改善後のlighthouseのPerformanceスコア

自分が貢献できる箇所を探し出し、小さな改善を複数行い、最終的に大きなインパクトを作り出すことができました。
振り返ると、分析と可視化をちゃんとステップに含め、成果を修正前後で比較してわかりやすい形にできたのが良かったと考えています。

改善はもちろんこれで終わりではありません。継続して課題の可視化と解決に取り組んで行きます! 最後まで読んでいただきありがとうございました。

Stripeを使って自社マーケットプレイスに決済機能を実装しました

こんにちは、freeeのPublic APIチームでエンジニアをしているまっつーです。

ビールの美味しい季節ですが外で飲めないのが残念です。

僕らのチームは2021年7月にfreeeアプリストアに決済機能をリリースしました。 https://corp.freee.co.jp/news/paid-app.html

この記事では決済の基盤としてStripeを利用した理由、どこに使ったのか、よかった点 / 仕様に合わなかった点を書きます。

アプリストアについて

freeeアプリストアとはfreeeのPublic APIを利用したアプリを公開できるマーケットプレイスです。

2021年4月には掲載アプリ数が100を突破しました。 https://corp.freee.co.jp/news/appstore-100.html

freeeと他サービスの連携を行うアプリや、freeeの機能を拡張するアプリをアプリストア内で検索し、自分の所属するfreeeの事業所とアプリを連携することで、より業務を効率化することができます。

また、開発者はfreeeアプリストア上でアプリを作成し、公開することでfreeeユーザーに対してアプリを提供することができます。

今まではfreeeアプリストアには決済機能がなかったので、開発者は無料でアプリを提供するか、自分でアプリ内に課金機能を実装する必要があり、マネタイズが難しい状況でした。

実現したかったビジネスモデル

freeeアプリストアの決済機能は、プラットフォーム型のビジネスモデルを実現するために実装しました。

アプリ開発者は、決済機能を利用してアプリ販売が可能になります。ユーザーがアプリを購入し、決済を行なったタイミングで、freeeの手数料を差し引いた金額をアプリ開発者が受け取ります。

1 アプリ開発者が有料アプリを公開する、2 アプリユーザーが有料アプリを購入する、3 freeeアプリストがプラットフォーム手数料をもらう、4 アプリ開発者がアプリ利用料を受け取るというビジネスモデルを図でしめしたもの
freeeアプリストアのビジネスモデル

Stripeを選んだ理由

freeeでは社内でも決済基盤をもっていて、freee会計やfreee人事労務上での決済は社内の決済基盤を利用して行っています。

なのでfreeeアプリストアに決済機能を実装するとなった時も、Stripeを利用する方法の他に社内の決済基盤を利用する選択肢もありました。

しかし、今後どんどん増えていく予定の3rd party製のアプリの課金情報を自社の課金基盤に登録すると管理が複雑になってしまうことが予想されたのと、後述するStripeの用意しているStripe Connectという仕組みがプラットフォーム型のビジネスモデルを素早く実現するのに適しているという主に2点から、Stripeを利用して実装することを決定しました。

Stripeを利用した部分

決済機能のリリースでfreeeアプリストアでは、アプリ開発者とアプリ利用者のそれぞれにたいして以下の機能を実装しました。

  • アプリオーナー
    • Stripeアカウントfreeeアカウントを連携できる
    • Stripeダッシュボードで作成したPrice を使って、アプリストア上でアプリに商品を登録できる
    • アプリストア上でトライアル期間の設定ができる
  • アプリユーザー
    • アプリストアホームで無料、有料のアプリの判別ができる
    • アプリ詳細ページでプランの詳細がわかる
    • 有料アプリのトライアルができる
    • 有料アプリの購入ができる
    • 契約中プランの変更ができる
    • 契約中プランの解約ができる
    • 請求書、領収書のダウンロードができる

Stripeを利用したのは太字の部分の機能です。

開発者側の商品、料金の設定、利用者側の購入、プランの変更、解約など決済まわりの機能はほとんどStripeを利用して実装しました。

アプリ開発者がアプリを有料販売をするときは、Stripe上でPriceと呼ばれる商品を作成し、そのidをfreeeアプリストアのフォームに入力して公開申請するだけで、有料アプリとして公開することができます。

アプリストアの商品設定ページの定額API IDというラベルの入力フォームに矢印を指してStripe dashboardで確認できるidを入力と書いてある図
idを入力するだけで設定が可能

アプリ開発者は、アプリ内で決済処理を実装することなく、有料販売することができます。Stripeを利用するメリットの一つです。

また、freeeアプリストアとStirpeでの二重管理を避けるため、商品の金額などの情報はfreeeアプリストアでは保存せず、Stripe APIを叩いて取得するようにしました。

一方でアプリを購入する前に一定期間無料でアプリを利用できる、いわゆるトライアルの機能はfreeeアプリストアに独自で実装しました。

Stripeにもトライアルの機能はあります。 https://stripe.com/docs/billing/subscriptions/trials

しかしStripeを利用してトライアルを行う場合には、アプリを利用する前にクレジットカード番号の登録が必要でした。

B to Cのビジネスであればトライアル前にカード番号を入力させることは一般的だと思います。

しかし、B to Bのビジネスにおいてはクレジットカード番号を入力するというのはハードルが高いです。

会社のクレジットカードを使う場合、社内申請が必要になることでしょう。

もっと気軽にトライアルをしてもらいたいと考えたので、結果freeeアプリストアではトライアル機能を自前で実装することにしました。

Stripeの良かった点

Stripe Connectをつかうことによってcredentialの考慮が減る

freeeアプリストアに決済機能を実装するにあたって、商品の情報やアプリユーザーの情報をアプリ開発者のStripeアカウントに紐づけるため、アプリ開発者が持っているStripeアカウントに紐づくリソースをStripe APIを使って操作する必要がありました。

他人のアカウントに紐づくリソースを操作する場合は、API IDやSecretなど、慎重な取り扱いが求められる情報を預かる必要があります。

しかしStripe Connectを利用していると、Stripe上でプラットフォームアカウント (=freee)とConnctedアカウント (=アプリ開発者)として紐づいていれば、freeeの所有するStripeアカウントのclient secret + アプリ開発者のStripeアカウントのidをheaderに指定することで、アプリ開発者のStripeアカウントのリソースを操作することができます。

https://stripe.com/docs/api/connected_accounts

アプリ開発者からcredentialを預からなくてすむため、実装する上ではかなり楽でした。

実装コスト0でユーザーにリッチなダッシュボードを利用してもらうことができる

アプリ開発者にはStripeにログインして、ダッシュボードから販売管理や売り上げの入金を行ってもらう必要があります。

ダッシュボードではユーザーごとの契約者の情報、契約状況、請求状況などを確認することができます。

Stripeのダッシュボードのスクリーンショット
Stripeのダッシュボード

決済基盤にStripeを採用したことで、freee側で実装を行うことなくアプリ開発者にリッチなダッシュボードを利用してもらうことができるようになりました。

仕様に合わなかった点

トライアルにクレジットカードの登録が必要だった

上ですでに述べましたが、Stripeのトライアル機能を利用しようとするとクレジットカードの登録が必要でした。

これは今回のfreeeの要件とはマッチしませんでした。

dashboardからの操作の考慮が必要だった

決済基盤としてStripeを利用しているので、商品や契約のデータはStripe側に保存されています。

基本はfreeeアプリストアからStripe APIを叩いてデータを取得する実装にしていますが、アプリ開発者がStripeダッシュボード上からデータを削除したり、プラットフォームアカウントとConnectedアカウントの紐付きを解除してしまうとAPI callがエラーになってしまいます。

Stripe APIを叩く処理は常に異常系として上記のパターンを考慮しながら実装する必要がありました。

これらの仕様はStripeの利点と表裏一体だと思います。

最後に

freeeアプリストアに決済機能がついたことによって、Stripeアカウントがあればアプリ側で決済機能を実装することなくアプリを販売することができるようになりました。

ぜひfreeeのPublic APIを利用してアプリを作成し、freeeアプリストアで販売してみて下さい!

全サービス新デザイン!リブランディングの裏側

こんにちは、DevBrandingのellyです。7月30日に配信した「freee Tech Night 〜全サービス新デザイン!リブランディングの裏側」の様子をご紹介します。

今回からfreee tech nightも心機一転、新デザインに変更しています。

freee tech night ロゴ新デザイン
freee tech night ロゴ新デザイン

freeeは6月22日にリブランディングを実施しました。それに伴い全サービスを新デザインにアップデートすることとなり、今回はアップデートに対応したエンジニアと UXデザイナーをゲストに迎え、大規模な変更や改善を乗り越えるコツについて詳しく話してもらいました。

登壇者集合写真
ライブ配信時の登壇者集合写真

ymrl: 写真左上 2014年1月入社 エンジニアとして入社し、現在はUXデザイナーとしてfreeeのデザインシステムを構築。 アクセシビリティ向上にも取り組む。

keik: 写真右上 2017年1月入社 アプリケーションエンジニア。 freee人事労務の開発を担当。 機能開発よりも品質・生産性マネジメントをしていることが多い。

のぶじゃす (@noblejasper): 写真右下 ラジオパーソナリティ、2017年に中途入社 mixi、ソーシャルゲーム企業でソフトウェアエンジニアを経験し freee に。入社後はエンジニア→エンジニア採用担当→エンジニアと DevBranding を担当。 しゃべりたがり。声が大きい。

リブランディングに伴いプロダクトの名前、ロゴ、色を変更

今回のリブランディングで2人は主に何を担当したのですか?

ymrl: 6月22日に新ビジョンのブランドイメージ”誰もが自由に経営できる統合型経営プラットフォーム”を発表していて、それと同時に各プロダクトのロゴが変わり、ネーミングルールも”会計freee”→”freee会計”のように”freee”が前に来るように変わりました。ロゴの色も鮮やかな色に変わっています。

freeeリブランディング概要

プロダクトの数はどれくらいあったんですか?

keik: 規模の大小はあるものの、全部で50個くらいあってかなり大がかりな動きだったと思います。

ymrl: 当日デプロイしたものでいうと14個でした。

激しいですね。ステークホルダーの数も多いし日程調整とかも大変だったのではないですか?

ymrl: freeeのブランドチームとTakramさんというデザイン会社さんに入っていただいて、freeeとして実現したいことを言語化して作り込んでいました。 そこから「誰もが自由に経営できる統合型経営プラットフォーム」というビジョンが決まり、デザインフィロソフィーも作っってそれをプロダクトに反映していくんですが、各プロダクトにそれぞれプロジェクトマネージャー、エンジニア、デザイナーがいて、私みたいに全部を横断してる人やQAがいて、50人以上は関わっているでしょうね。

その合意を取りながら進めるのはヤバそうですね。 keikさんはどんなことをしていたんですか?

keik: 私はfreee人事労務のロゴ、文言、色の実装を担当していました。

ymrlさんが配ったものを実際に適用していったということですね。

keik: 実装作業は私が行い、その変更のレビューは別のメンバーにしてもらうという感じでしたね。

工数はどのくらいかかりました?

ymrl: リブランディングのプロジェクト自体は去年始まって、去年の年末に社内に発表されました。プロダクトの作業は今年の2月からスタートして、最初にブランドチームとUXチームで発表当日に何をどこまでやるのか、その先どうするのか、何をやって何をやらないみたいなことを決めて、4月からロゴとかのファイルを配る準備をしてました。UIの色変更は2ヶ月くらいかけて5月半ばまでやっていました。

それで6月22日リリースですよね?(笑)

ymrl: はい(笑)freee会計とかfreee人事労務みたいにユーザー数が多いプロダクトは発表当日にやり切りました。

デザインシステム”Vibes”をアップデートしデザインを一括変更

ymrlさんはデザインシステム”Vibes”(社内の標準UIコンポーネントライブラリ)のオーナーですよね。それが今回のような一括変更にどう役立ったのか教えてもらえますか?

keik: そうですね、めちゃくちゃ助かりました。Vibesが適用されている部分はそちらのバージョンアップだけ行えば後は何もしなくても変わるという状況なので。

Vibes 自体をymrlさんが直すときはどうでしたか?

ymrl: はじめは元々のパレットを入れ替えればいいかなと思ったんですけど、ブランドイメージががらっと変わって、これまでは主張の激しくない控え目な青色だったのが今回軽やかな明るい青色になったので、これまでと同じ使い方をすると画面がちょっと眩しいというか、ぎらぎらした感じになってしまいました。

2カ月くらいかかったと言っていたのはそこの調整ですね。 これまでのUIとほとんど変わってないように見えつつも、実は青色だったところを控え目なグレーや黒に変えた場所があったりします。一番分かりやすいのはヘッダーのグローバルナビゲーションが白くなったところで、ここはやっぱり白くないとダメだなぁと思って結構頑張りました。

登壇者写真
freeeのデザインの根幹であるVibesの創始者ymrlさん

デザイナー側は開発段階でどんな準備をしていましたか?

ymrl: デザイナー側は細かく変えていくというだけで、Vibes も実装に対してはあんまり頑張った場所はなく、色を置き換えたものを粛々と作ってました。ただ、さっき言ったようにどこに何色を配置するというのを結構変えてるので、なるべくコードが増えないようにしつつ両バージョン存在する状態にするというのはちょっと頑張んなきゃいけなかったなと。

Vibes適用外の部分はカラーコードを定数化、正規表現置換でいつでも再生成可能にすることでメンテンナンスコストを最小に

エンジニア側ではどういう工夫をしましたか?

keik: Vibesを使ってる部分は、全然何もしていないです。ただし、Vibesの適用は進行中でプロダクト全域に対して適用されているわけではないので、適用されていない部分は根気強く grep をするというガッツに満ちたやり方をしていました(笑)

プロジェクト自体は3月から聞いていて、実際に色が決定したのは5月だったんですが、色は追って統一されるということを見越して、ハードコーディングされているカラーコードをgrepしてかつそれを定数に置き換えておけばどんな色がきても対応できるだろうってことでコツコツ定数化していくというのを積み重ねていきました。

それは便利ですね。とはいっても5月までの間途中で入った修正とはどう戦ったんですか?

keik: あんまり早くブランチ作っても、コードの変更でコンフリクトが発生してブランチのメンテナンスにかなりのコストがかかってしまいます。やった工夫は、定数を使うのもそうだし、それをやってもコンフリクトするところはあるんで、正規表現置換で同じ趣旨に沿った変更を機械的にいつでも再生成可能なようにしておくということです。コンフリクトしたらそこを頑張って解消しにいくというよりは、捨ててもう1回作り直すってことを繰り返していましたね。なのでメンテナンスコストはほぼ発生してないです。

登壇者写真
相当大変だったはずなのに爽やかに淡々と語るkeikさん

実行すればいつでも変更ができるってことですよね。

keik: Gitのコミットメッセージにコマンド書いちゃうというので準備完了ですね。

ymrl: 最初にそういう工夫をしていたからだと思うんですけど、実を言うとfreee人事労務は着手が全プロダクトの中でもかなり遅いほうで、音沙汰が無いなぁと思って連絡したら「あ、来週からやります」と言われて、本当に翌週にはほぼ完成した状態になってて、作業量が減るように準備して後から一気にやるというやり方が本当にうまくいったんだなと思いました。 全体見ている側からすると心配になるやり方なんですけど。

心配だったんですねymrlさんは(笑)

QAと連携することでfreee人事労務ならではの変更点の多さにも対応

freee人事労務は他のプロダクトと違ってロゴがもともと緑色で、色を変えるだけで見栄えが悪くなったり使い勝手が損なわれることもあったのではないかと思いますが、そこはどう工夫していましたか?

keik: Vibesが適用されているところはそこも含めて色設計してくれていたので安心だったんですけど、それ以外の部分に関しては確かに機械的にやると変な感じになるリスクもあるので、QAのチームに依頼するという形をとりました。 今回のようなケースだと仕様がしっかりあるわけではないので、何かおかしいところないか?というあいまいな依頼の仕方だったんですけど、それでも意思疎通できるしやってくれるチームなので助かりました。

リリーススケジュールの整理で障害発生時にも臨機応変に対応

freee会計もfreee人事労務も一気にリリースしたんですか?

ymrl: ブランドチームからするとドンと行きたかったと思うんですけど、プロダクト側に話が来た時点で「一斉には無理なんでその日のうちに出せるぐらいの気持ちでいてください」って言いました。

そうですよね、危ないですよね。

ymrl: 危ないですね、そもそも普段からそんなに同時にデプロイしてないので何が起きるか分からないです、2~3個はあっても14個はないと思うので。

適切なコミュニケーションができていていいですね。リリースの順番もymrlさんが整理したんですか?

ymrl: そうです、気づいたら誰もやってなかったので(笑)リリースの1週間前ぐらいに何時何分にこれをデプロイというスケジュールをまとめたドキュメントを作成しました。インフラについては詳しくなくて「これとこれはインフラを共有しているので同時にやらない方がいい」みたいな指摘をもらったんですけど、そういう会話のためにもタタキを最初に作ったのは良かったなと思います。

実は危ない瞬間もあって、リリース開始のちょっと前に CIがコケたり、オペミス未遂みたいなのが発生して、とっさに順番入れ替えたりしたんですが、そのときもこのドキュメントを見ながら、これを後ろにずらそうみたいな会話ができてよかったです。おかげでなんとかリリース開始時刻までには間に合いました。

keik: いやあ、あれは焦りましたね(笑)デプロイのパイプラインに使っているSaaSに障害が起きてデプロイできなくなったっていうのが真相なんですけど、祈ってましたね。みんなこんな絵文字使って🙏

リブランディングを通してコードの品質やメンテナンス性が向上しUIデザインもしやすくなった

やってみて良かったこと、悪かったことはありますか?

keik: 技術的な観点で言うと、統一したり一貫させて実装することがコードの品質に寄与していて、今回の作業によってハードコードされているところはほぼなくなり、品質やメンテナスの観点でもすごく良くて、式年遷宮みたいな効果があったなと思っています。

これからいつでも色を変えられますね。

keik: 全然すぐいけます、紫色とか。

いや、しばらくはこの色でいきたいです(笑)ymrlさんはどうですか?

ymrl: freeeがブランドとして目指しているものとかビジュアルの方向性が整理されたので、今後UIを作っていくのがやりやすくなったと思います。

全体を俯瞰できる環境を整えること、段取りを整え最適な方法を事前に発見しておくことが大規模変更を成功させるコツ

複数のプロダクトをまたいだ大規模な改善やアップデートに立ち向かう場面での教訓はありますか?

ymrl: 一気に全てを変えるっていう今までやったことがないことをやったので、どう立ち回るとスムーズにいくのか分からないまま飛んできたボールを拾って打ち返すみたいなことをやってて、こういうときには全体を俯瞰して見る人が必要だし、早めにkeikさんみたいに動いてくれる人を巻き込んでいくのが必要だと分かって良い学びでした。

エンジニア向けで言うと、今回最初に自分がある程度エンジニア視点での整理をやったんですけど、自分もエンジニアではない立場になって2~3年経って今の構成とかチームの状態が分からなくなっていると感じました。こういう巨大デプロイをする場合、関係者が多いので特にエンジニア起点でタスクが発生していない場合はエンジニアが早めに拾いに行ける体制があると上手くいくんじゃないかなと思います。

keik: 各プロダクトの事前の仕込みが功を奏した部分もあったので、段取りを整えて適切なやり方を発見しておくということがすごく重要かなと思います。

この二人のおかげもあって今回リブランディングを無事に成功に導くことができました。 リブランディングで生まれ変わったfreeeをこれからもどうぞよろしくお願いいたします。

イベントの本編はここまでです。この後は「アフタートーク」でより深い技術談議が繰り広げられました。

▶今回のイベントのアーカイブ(録画)


www.youtube.com

▶デザインシステムVibesついてはこちらのblogで詳しく紹介しているのでご興味ある方はこちらから

developers.freee.co.jp

技術による課題解決のプロセスの一例を紹介

メンバーで相談している様子

こんにちは、okoshiです。

前回の私の記事は大変たくさんの方に読んでいただいて大変感謝しております。

今回はカッとしてシュッとやった一例を紹介させていただきます。 私の担当していたfreeeプロジェクト管理で、Git操作時にhuskyを用いて小さな課題を解決したというストーリーです。

ご注意

本ページでは、freeeプロジェクト管理でhuskyを採用してみたという話であることと、導入当時とバージョンが変わってしまっているため本ページではインストール方法や使い方までは解説しません。ご了承ください。

小さな課題とは

freeeプロジェクト管理ではバックエンドにRubyを採用していますが、使用するエディターはメンバーによって違い、Vimを使う方もいればRubyMineを使っている方もいます。 メンバーの中にVisualStudio Codeを利用している方もいて、その方はVisualStudio CodeのプラグインでRubocopを自動で実行するようにしていました。 Rubocopとは静的コード解析をする機能を提供するものですが、動作が非常に重く開発速度に支障が出てしまっていました。

我々のチームでは、画面を共有してモブプログラミングをすることが多いのですが明らかに動作が重く、なんとかしなければチームの開発能力が落ちてしまう懸念がありました。 エディタを変更することも考えましたが、Vimの操作になれてもらうことや、RubyMineで解決できるかは検証が必要(PCのスペックの問題かもしれないため)であり早々に諦めました。

CIツールを使用しているので、実行中に指摘がされるためVisualStudioコードで使用しているプラグインを使用しないということも考えました。しかしCIから指摘されるのを待つことになり、数分間作業を待つか、別の作業をしながら待ったとしてもコンテキストスイッチが負担になるという別の課題が発生します。

ローカル環境で、自動でRubocopを実行するにはどうしたらいいか、ということが課題でした。

過去の事例から思いついたアイデア

長くエンジニアをやっていると、過去の事例がサッと頭に浮かびます。 その過去にあったことというのは、マネージドサービスとして使っているCIツールを大人数で使っていたため渋滞していたり、費用がかかり過ぎているという課題があり、ワークロードを減らしたいという思いから、静的コード解析程度のことはローカルで実行しようとなったことです。

その対処方法はこうでした。

  • 全員Gitを使っているのでGitフックを使ってコミット時にチェックするようにしよう。
  • huskyというnpmライブラリーを使えばGitフック時にプログラムを実行できるので、それを利用して実現しよう。

このアイデアは私が出したものではないですが、このアイデアを出してくれた人もまた別の現場で経験したことから採用したものでした。

この経験から、Gitコミット時であれば自動でRubocopを実行できるので課題を解決できるし、しかもすぐに対応できると思い、シュッとやってみることにしました。

なぜhuskyなのか

なぜそのままhuskyを採用するに至ったかといえば、先述の通り使った経験があったというのもありますが、有名なOSSであるという理由もあります。 有名なOSSであるということは評価が高いとか、よく保守されているだろうから今後も安全に使えるだろうと考えることができます。

huskyは他のメンバーが使い始めるための考慮もされており、yarn installするときに同時にhuskyをインストールするため、導入してみたはいいものの、ある人は使っているけどある人は使っていないといったことが起きにくいように考えられています。とてもありがたいですね。

メンバーから上がった懸念

huskyによってGitでコミットするときにpre-commitをフックし、lint-stagedというnpmライブラリーを実行し、lint-stagedがRubocopを実行するという仕組みをとっています。

導入することをメンバーに相談したところ、コミット時にプログラムを実行するということは、それだけ時間がかかるようになるのではないかという懸念メンバーから上がりました。実際10秒程度はかかることがあります。

しかし、実行時は何が起こっているのかを視覚的に確認できるので対して遅く感じないことがわかっていたので、実際に作業をしている操作を見せることで受け入れてもらうことができました。

lint-stagedというのはhuskyとは別にインストールするnpmライブラリーなのですが、huskyとの相性がよくいっしょに使われることが多いライブラリーです。

どんな静的コード解析を行わせているか

ここで、我々がどんな静的コード解析を行わせているかを紹介しましょう。 package.jsonの一部を公開します。

    "*.{rb,rake,ruby}": [
      "bundle exec rubocop --force-exclusion -a", // ①
      "bundle exec rubocop --force-exclusion -a --only Style/FrozenStringLiteralComment,Layout/EmptyLineAfterMagicComment", // ②
      "bundle exec rubocop --force-exclusion -P" // ③
    ]

①〜③の行で3回もRubocopを実行していますが、すべて違うことを行っています。 先述の通り、コミット時の実行時間の懸念を意識して、--force-exclusionをオプションとして指定しています。これは、.rubocop.ymlでファイルを除外(AllCops.Exclude等)していても解析だけはしてしまうので--force-exclusionによってその解析もしないようにするものです。

①では、Rubocopが修正可能なコードは修正しています。例えばコードが始まる桁が2桁目からなのに、ある桁だけ3桁目から始まっているような場合に2桁目に移動させるといった感じです。 ②では、Rubyのコードの最上部に# frozen_string_literal: trueとその下の行に空行を挿入しています。 ③では、Rubocopによる静的コード解析を実行しています。-Pオプションによって複数のファイルで並行実行を行っています。-P-aは同時指定ができないので①と②では指定していません。

最後に

業務を遂行中に発生した課題から、それの解決に至るまでの思考プロセス、導入にあたってのメンバーのケアを簡単に紹介させていただきました。 今回紹介したのは非常に小さな課題ですが、freeeでは必要なことならメンバーがサッと動いてこういった改善を行っています。

そして、これを実現するにあたってとってもうれしいことがありました。実は”どんな静的コード解析を行わせているか”の②は、①と③を導入後に他のメンバーがこの仕組みを応用しより使いやすく改善してくれたものです。

更に今、別のメンバーからはフロントエンドサイドのLintも入れるかどうかという話も上がっており今後も進化しつづけそうな雰囲気がでてきています。

小さな課題の解決がメンバーの刺激になって継続的な改善につながっていく。こんなチームにいることができて最高だなぁと感じています。

参考