新卒2年目エンジニアが振り返る、チーム開発でやってよかったこと・やればよかったこと
この記事はatama plus Advent Calendar 2023の8日目の記事です。
はじめに
こんにちは。新卒2年目エンジニアのyutake27です。
今年の3月から7月にかけて、Vue 2で構築された社内ツールをReactに置き換えるプロジェクトに参加しました。これまでの業務では既に色々と開発環境が整備されているリポジトリでの開発をしており、ディレクトリ構成やCI整備など初期段階から考えるようなプロジェクトに関わるのはこれが初めての経験でした。
この経験を通じて、チーム開発では手動の繰り返し作業が発生しないよう自動化したり、方針をドキュメント化したりすることで、本質的な作業時間を増やし、チーム全体の作業の効率化を実現できることを学びました。
今回の記事では実際にプロジェクトの中で行ってみて効果的だった行動と振り返ってみると取り組むべきだったことについて共有します。少しでも参考になれば幸いです。
前提: プロジェクト概要
どんなツールか
教材コンテンツを入稿するエディタツールです。主に社内のユーザーが使用します。
サーバーとの通信はほぼ発生しない、フロントエンドのみで構成されていデスクトップアプリケーションです。
プロジェクト目的
もともとツールはVue 2で作成されていました。
Vue 2のEOLが2023年末だったのでReactにリプレースすることを目的としたプロジェクトです。
プロジェクト規模
チーム構成はWebエンジニア3人、QA1人の少人数体制で、プロジェクト終了まで約5ヶ月程度かかりました。プロジェクト開始2ヶ月程度でエンジニアが1人入れ替わりました。
技術スタック
- Language - TypeScript
- Runtime - Node.js
- Frontend framework - React
- UI Library - Material UI
- State Management - valtio
- Build tool - Vite
- Test - Vitest
やってよかったこと
ここでは実際にプロジェクトで実施して良かった行動とその効果について紹介します。
linter, formatter設定
チーム開発において定番ではあると思いますが、linter, formatterで書き手によるコードのブレを減らすようにしていました。
linter / formatter については全てのルールを説明するのは現実的ではないので、ここでは特に使っていて効果的だと思ったルールについて説明します
no-restricted-importsの利用
特定のimport先に対してエラーを吐くようにすることで、importの制限を設定していました。
プロジェクトでは、ライブラリのコンポーネントをラップしてカスタムコンポーネントを提供する場合に、直接使用してほしくないライブラリのコンポーネントのインポートを禁止し、誤って使用するのを防いでいました。
import/no-restricted-paths の利用
↑のルールと似ていますが、importの制限ゾーンを定義して特定のパスに一致するファイルからのimportを禁止することで、モジュール間の依存関係を定義して、依存関係が一方通行となるように制限したり、あちこちでglobal stateにアクセスしないようにアクセスできる場所を制限したりしていました。
linterには他にも様々なルールが存在するので便利なものはどんどん取り入れていきたいです。
pre-commit設定
Lefthookを用いてコミット前にlintやformat, コンパイルが自動で行われるようにしていました。
失敗した場合、コミットできないのでコミット前にコードに問題があることに気づけて良いです。
huskyなどのツールもありますがLefthookは並列実行が可能なので速度が早いです。個人で設定のオーバーライドも可能なのでpre-commitが遅く待てないという人はスキップすることもできます。(CI上でもlint, formatを実行していたのでローカルでスキップしてもそこで気づけます)
プルリクエストテンプレートでチェックリスト作成
プルリクエストテンプレートを使って、プルリクエスト作成時に確認すべきことをチェックリスト化していました。
チェックリストは以下のようなものを使用していました(一部抜粋&改変)
- テストの要否判断
- [ ] 作成した
- [ ] 不要
- 表示確認
- [ ] 確認した
- [ ] 不要
- ビルド版の動作確認
- [ ] 確認した
- [ ] 不要
これによって、レビュー依頼する前にバグに気づいたり、必要なタスクの漏れに気づいたりすることができました。
時間の経過とともに、若干形骸化しやすい面もあるので、定期的にチェック項目をアップデートしていくのが重要そうです。
プルリクエストのレビュー自動アサイン
少人数での開発かつ1つのリポジトリを丸ごと使用した開発だったので、GitHubのチームへのレビューアサイン機能とコードオーナー機能を組み合わせて、PRが作成された際には自動でチームメンバーの誰かがランダムにレビュアーに設定されるようにしていました。
チームへのレビューアサイン機能は以下の記事を参考にしました。
チームメンバー全員にレビューをアサインしたり、チームメンバーからランダムに選んでアサインしたり色々とカスタマイズできます。(ランダムに選択する場合割り当て方式も選べます。)
コードオーナー機能を用いると指定したディレクトリ以下が変更された場合、コードオーナーに自動でレビューがアサインされます。
リポジトリ全体に対してチームをコードオーナーとして設定することでPR作成時に自動でチームメンバーがレビューにアサインされるようになります。
レビュアーのアサインは大した作業ではありませんが、いちいち人を探して設定するのも面倒なので自動でアサインされ楽になりました。
JiraとGitHubの連携
弊社ではJiraでタスクの管理をしています。
今まで、「PRをマージしたら手作業でJiraのチケットのステータスを更新する」ということをやっていました。
手作業でやっていたので更新忘れが発生したり、後でマージするといって先にチケットをDONEにして肝心のマージをし忘れたりと実際のステータスとチケットのステータスが一致していないことが多々ありました。
そんな中で↓の記事を発見しました。
JiraとGitHubを連携してチケットのステータスと実装のステータスを同期するというものです。
これを利用して以下のようなルールを設定することで自動でチケットのステータスが更新されるようにしていました。
(1) ブランチ作成時: TODO -> DOING
(2) プルリクエスト作成時: DOING -> IN REVIEW
(3) マージ時: IN REVIEW -> DONE
これによってチケットステータスを更新する手間が省け、チケットの進捗がリアルタイムに正確に反映されるようになりました。
ドキュメント化
README作成
当たり前感がありますが、開発の前に必要な環境構築の手順やブランチ戦略など基本情報をREADMEにまとめていました。
READMEは目につきやすく参照されやすいので後からチームに参加した人が必要な情報にアクセスできるようにすると良いです。(が、ちゃんとREADME運用できていたかというと怪しいです、、)
おおよそ以下のような構造にしていました。
# プロジェクト名
## セットアップ手順
### node環境の設定
### pre-commit設定
### moduleのinstall
## Quick Start
### 起動
### 開発者ツールについて
### build
### test
### lint
## コード運用
[システム構成の詳細はこちら](./ARCHITECTURE.md)
### 📂ディレクトリ構造
### サブモジュール
### ブランチ戦略
### Auto Update Process
## 本番環境のエラーログの見方
READMEにすべての情報を書くと肥大化してしまうので、次のセクションで触れる設計、実装方針などはarchitecture.md
を作成して、READMEからリンクを貼り参照できるようにしたり、分割できるものは別途markdownを作成してREADMEから参照するようにしていました。
設計、実装方針のドキュメント化
コードの書き方の方針(ファイル名形式やディレクトリ間の依存ルールなど)をドキュメントとしてなるべく残すようにしていました。
これによって後からなぜこのような書き方にしたのか忘れても理由をすぐに参照することができます。
また、新たにメンバーが加わった際にもコードの書き方に関するオンボーディング資料になります。メンバーが追加される時に資料を作り始めると昔の議論を掘り起こしたり、ドキュメントにまとめたりするのに時間と労力がかかるので、議論があった際に都度まとめておくと良いです。
(実際はドキュメント化の運用を仕組み化できておらず、ドキュメントにできてない内容もありました。。ドキュメント化を忘れない良い仕組みがあればぜひ教えてください。)
やればよかったこと
ユニットテスト作成
ユニットテストを全ての箇所で作成していくのは時間がかかります。
そこでユニットテスト作成が必須の部分は先に洗い出し、その他の部分はテストの重要性を都度判断して必要そうだったら作成するという方針にしていました。
ですが、作成の基準が明確ではなかったので結局あまりユニットテストは作成されませんでした。(Vue2からReactへの移行プロジェクトだったので、UIの変更がメインでロジック部分にはほとんど手を入れていなかったというのもテストが増えなかった要因ではあります。)
移行プロジェクト終了後、新規機能を追加するタイミングで既存のロジックへの影響判断が難しい状況に遭遇したので、ロジック部分のテストは今後の開発を見越して追加するべきでした。
フロントエンド開発のためのテスト入門の本にも
筆者は、チームにテストを書く文化が根づくか否かは、初期設計段階で決まると考えています。コードが小さいうちから方針を示しておくことで、どのように書けばよいのかという共通認識が生まれます。前例をコミットしておけば、テストに不慣れなメンバーでも、前例を参考にある程度のテストが書けるようになります。
と書かれているので、プロジェクト初期段階からテストを作成する流れを作れればよかったです。
レビュー観点の共有知化、仕組み化
個人の反省になりますが、レビューを受けて学んだことをチーム全体の知識やコードの方針として還元する動きをすれば良かったです。
例えば「Reactではハンドラを渡すpropsはなるべくonで始めた方が関数を渡すことが分かって良い」というレビューをもらったとします。
この指摘は自分だけでなく将来的にチームの他のメンバーも受ける可能性があります。(自分がそのレビュー内容を忘れて再度同じ指摘を受けるという可能性もあります)
繰り返し同じ指摘が発生するのは、チーム全体の生産性低下につながります。
レビュー観点としてまとめておけば後から他のメンバーも参照でき、将来的に他のメンバーがチームに加わった場合も活用することができます。
もしくは、同じ観点でのレビューが再度発生しないよう、linterでそのようなコードがそもそも生まれないよう仕組み化するという手もあります。
仕組み化が実現できれば、個々人がドキュメントを参照しながら気を付けて実装する運用よりも、よりコードの一貫性を保ち、レビュープロセスの効率を高めることができます。
なので今後はlinterで表現できそうな観点は積極的にルール化していきたいです。
終わりに
総じて、効率よくチーム開発するためには
- 面倒な手動による繰り返し作業が発生しないように自動化
- 後から参照できるように方針をドキュメント化
の2点が重要だと感じました。
linter, formatterで書き手によるコードのブレを減らすことができればレビューでの指摘を減らすことができます。JiraとGitHubを連携すればJiraのステータスを手動更新する必要がなくなります。
このように、人のミスが発生しうる繰り返し作業を自動化していくことで本質的な作業の時間を増やせることを学びました。
また、チームで話し合って決めた方針はドキュメント化することでいつでも見返せるようになり、後から過去の検討スレッドを探し回る時間を省略することができたり、人による知識の差を減らすことができたりします。
これらの仕組み作りは可能であればプロジェクトの初期段階でできると、後から手戻りが発生せずに済んで良いと感じました。
他にもチーム開発でやると良いことがあればぜひ教えてください!
明日は kiyo0014 さんの「atama plus株式会社のアルゴリズムチームでインターンをした話」です。ぜひご覧ください!
Discussion