初めまして、アプリケーションエンジニアの杉浦(saba_can00)です!
本記事は、NewsPicks Advent Calendar 2022 の12/7 のブログとして記載させていただいています。
この記事でお伝えしたいこと
- Dockerを利用するようにしたら開発セットアップの時間が大幅に短縮できた!
- Local環境用にコメントアウトしていた箇所を環境変数で切り替えられるようにしたら、開発者体験が良くなった!
- 開発セットアップの時間を短縮することで、新しいメンバーの受け入れがスムーズになったよ!
ざっくり状況の説明
プロダクトを内製化がスタート
外部の開発会社さんに委託していたシステムを今年(2022年)から内製のチームで引き取って、開発することになりました。
ちょこちょこ設計やいわゆるプロジェクトマネジメント、PRレビューなどでプロダクトのコードについては触れていたもののきちんと向き合ったのは今年の夏頃が初めてでした。
引き取った際のLocal環境のセットアップの状況
セットアップドキュメントを見たり、元々開発してくださっていたエンジニアの方にちょこちょこ質問しながら、僕も含めてセットアップを始めてからアプリケーションがLocal環境で動くまでに5-8時間ほどかかっていました。
なぜ、そんなに時間がかかっていたのか?
いくつかの要因が重なっていたのですが、Localセットアップでよくある次のような状況が起きていました。
- セットアップドキュメントのいくつかの内容が古くなっており、いくつかの手順が現在のアプリケーションの構造と異なる状態になっていた
- postgresやredisなどのミドルウェアをそれぞれがlocalにインストールしており、versionによってはうまく動かないことがあった
- 実行環境であるNodejsのversionを統一する仕組みが存在しておらず、versionによってはうまく動かないことがあった
- Loggerなどの一部の機能については、外部リソース(Newrelicなど)を利用しているためにLocal実行用にコードを一部コメントアウトしたり書き換えが必要になっていた
- マイグレーションツールの
sequelize-cli
とsequelize.sync()
が両方利用されており、手順を間違えるとテーブル構造が正しく作成されない状態になってしまっていた(これはレアケースだと思います。)
これらの課題がありつつも、すでに開発しているエンジニアにとってはすでに通り過ぎた道で現状は特に困っていない状態だったので機能の開発等の別のタスクを優先させている状況でした。
どのように改善していったか?
内製のチームで引き取るにあたって、これからジョインしてくれるメンバーが最初に行う作業、かつ開発者体験に大きく関わる部分なので、最初に改善を行うことにチームで決めました。
以下どのように改善していったかを共有させていただきます。
1. セットアップドキュメントを新しく更新
Confluenceのドキュメントで手順が古くなっていたものは、新しいものに更新しました。 ただ、ドキュメントの更新だけだとまだまだハマりポイントが多いので、次で紹介させていただくような取り組みとセットでドキュメントの改善を行なったことが時間短縮に大きく貢献したと思っています。
2. Dockerを利用して、Local環境にミドルウェアを導入するように改善
もともと、個々の開発者がLocal環境にセットアップしていたpostgresやredisなどのミドルウェアをdockerを利用するように変更しました。 具体的には以下のようなdocker-compose.ymlを用意して、docker composeからpostgresやredisを起動できるようにしました。
docker compose up
とするだけでミドルウェアの起動ができるので、サーバー起動の手間もなくなり早めに着手してよかったと感じています。
[sample-docker-compose.yml]
services: postgres: image: postgres:13.7 ports: - "127.0.0.1:5432:5432" environment: POSTGRES_PASSWORD: xxxx volumes: - postgres_volume:/data networks: - app redis: image: redis:7.0 ports: - "127.0.0.1:6379:6379" volumes: - redis_volume:/data networks: - app
コンテナを利用していたら当然のことではあるのですが、docker imageを利用することで、個々の開発者間でのversionの差異がなくなりセットアップ時に「どのversionを使えばいいのでしょうか?」と言ったやりとりが不要になりました。
また、versionが異なっている場合のみ発生するエラーなども避けられるようになり新しくジョインしたメンバーがセットアップ時に不要にエラー調査しなければならないということも無くなりました。
3. Nodeのversionを.node-version
により統一
実行環境であるNodejsについても、.node-version
によりversionを統一するようにしました。
チームではnodenvやanyenvに慣れている人が多かったため、.node-version
を採用することにしました。
今後、asdfを利用したいという声がチームでも高まったら、.tool-versions
に置き換えるか、.asdfrc
のlegacy_version_file
の設定変更をするかを検討してみようと思います。
4. Local環境用にコードの書き換えが必要な部分については環境変数で切り替えられるように実装を変更
外部リソース(Newrelicなど)を利用しているためにLocal実行用にコードの書き換えを行う必要があった部分については実行環境の環境変数を利用するように実装を修正しました。
以下はサンプルですが、APP_ENV
のようなアプリの実行環境を表現する環境変数を利用しています。
const getLogger = () => process.env.APP_ENV === "local" ? getLocalLogger() : getProductionLogger()
Nodejsだと、NODE_ENV
があるのでそちらを利用するべきではという意見もあったのですが、Next.jsなどいくつかのライブラリではNODE_ENV
に入る値を仕様として定めているのでそちらと将来的にぶつかる懸念があると嫌だねということで、NODE_ENV
ではなくAPP_ENV
のような環境変数を新規で導入しています。
検討にあたっては以下の記事を参考にさせていただきました。
参考:
セットアップ時にコメントアウトを忘れてエラーになることがなくなったのはもちろんなのですが、Local実行用のコメントアウトはcommitする時にも「これはcommitしたらダメなやつだ」と開発者側で意識する必要があり、小さなストレスになっていたので、改善してほんとうによかったと思っています。
5. マイグレーションツールをsequelize-cli
に統一
これはあまり経験しない事例だとは思うのですが、マイグレーションツールのsequelize-cli
とsequelize.sync()
が両方利用されており、手順を間違えるとテーブル構造が正しく作成されない状態になってしまっていました。
その結果、
sequelize-cli
で途中までマイグレーション- 開発環境を起動して、
sequelize.sync()
でModelに対応するDBを生成 sequelize-cli
で最後までマイグレーション
のような複雑なマイグレーションを行う必要があり、ほとんどのメンバーが順序を間違えておりうまくLocal環境のDBを構築できないという状態でした。
これを改善するために、sequelize.sync()
で作成されていたModel用のスクリプトをsequelize-cli
ファイルで作り直すという改善を行いました。
改善としては以下の作業を実施しました。
sequelize.sync()
をなくし、ModelからDDLの作成・更新を行わないように変更sequelize.sync()
により生成されていたDDLを特定し、対応するDDLをsequelize-cli
ファイルで作成- すでに同様のテーブルが存在しているdevelopment環境、staging環境、production環境では
SequelizeMeta
(マイグレーション管理用テーブル) を操作し、すでにスクリプトが適用済みに設定
DBの定義を書き換えるため、produnction環境に影響を与えないかが最大の懸念でした。
幸い sequelize-cli
はSequelizeMeta
というテーブルでマイグレーションの実行管理をおこなっていたので、すでに同様のテーブルが存在しているdevelopment環境、staging環境、production環境では対象のsequelize-cli
スクリプトは実行済みに変更するという対応を行いました。
この結果、Local環境や新規で環境を構築する際に実行されるように影響範囲を限定でき、大きなトラブルもなくリリースができました。
Local環境では最終的にnpm scriptから sequelize-cli db:migrate
を実行すれば全てのDDLが問題なく実行できるようになったので、セットアップ時のテーブルが作成されない系トラブルはほぼ無くなりました。
結果、どうだったか?
改善の後に新規でLocal環境のセットアップを行なったメンバーがどのくらいの時間で完了したのかを計測しました。
結果として、平均25-30分弱で完了できたとの報告をいただいたので、およそ 1 / 10 にすることができました!
Local環境のセットアップが短くなったことによる心境の変化
Local環境のセットアップが大変だった時は、
- 開発を始めるのに必要だと分かってはいるものの、受け入れ側もサポートが大変
- せっかく入ってくれたメンバーに開発以外の作業で手間取らせてしまうのが心苦しい
- なかなか開発に入れないので新規メンバーのフラストレーションもたまる
など、受け入れ側も新しくチームに加わってくれたメンバーも辛いと感じる作業でした。
しかし、今回の一連の改善をすることによって、
- 新規のメンバーが入ってくれたその日にタスクをアサインできる
- 新規のメンバーも早く開発作業に入ることができるので、その分早く、成果を出してチームに馴染める
- 受け入れ側も1 -2つの質問に答えるだけでセットアップが完了するので、サポートを負担に感じない
などのいい効果がありました!
お客様に価値提供するための機能追加ももちろん大事なのですが、継続的にプロダクトを開発・運用していくためにもLocal環境のセットアップなど関わる開発者がモチベーションを高く開発できる仕組みづくりも一定の工数をとってやっていけるとすごくいいなとあらためて感じました。
すでにやっているよという開発チームは多いとは思うのですが、
- ミドルウェアのDocker での起動
- コメントアウトなどで対応しているLocal環境のみ利用したいコードを環境変数で切り替える
これらの対応はコストに比べて、得られるメリットが多いのでおすすめです!
以上です。
それでは素敵な開発者ライフを。