こんにちは!
コードをきれいに効率的に書く。
やらなければいけないことですが、とても難しいことです。時間をかければできるかもしれません。ただ、業務になると時間との勝負であり実現することは困難です。私レベルですと。。
なので、学びます。
下記の本を参考にしました。
レガシーコードとは
レガシーコードとは、一般的に以下のような特徴を持つコードのことを指します。
テストがない
自動化されたテストが存在しないため、変更が難しく、バグが発生しやすい。
依存関係が多い
他のコードやシステムに強く依存しているため、変更が困難。
可読性が低い
コードが複雑で理解しにくく、ドキュメントも不足していることが多い。
古い技術や手法を使用
最新の技術やベストプラクティスが適用されていない。
これらの特徴により、レガシーコードは保守や拡張が非常に難しく、開発者にとって大きな負担となります。また、この非効率による失敗でとてつもない損額が発生していることがCHAOSレポートによっても明らかになっている。
やり方より先に目的、理由、誰のためかを説明する
やり方より先に「目的」「理由」「誰のためか」を説明することで、プロジェクトやタスクの方向性が明確になり、チーム全体の理解と協力が得られやすくなります。これにより、プロジェクトの成功率が高まり、より効果的な成果を得ることができます。
1. 目的
目的を明確にすることで、プロジェクトやタスクの最終的なゴールがはっきりします。これにより、チーム全体が同じ方向を向いて努力することができます。
2. 理由
理由を説明することで、なぜそのプロジェクトやタスクが重要なのかを理解できます。これにより、チームメンバーのモチベーションが高まり、プロジェクトに対するコミットメントが強くなります。
3. 誰のためか
誰のためかを明確にすることで、プロジェクトやタスクの成果が誰にどのような影響を与えるのかを理解できます。これにより、ユーザーやステークホルダーのニーズに応じた最適な解決策を提供することができます。
小さなバッチを作る
「小さなバッチで作る」というプラクティスは、ソフトウェア開発において大きな機能やタスクを小さな部分に分割して進める方法です。これにより、開発プロセスがより効率的で柔軟になり、リスクを減らすことができます。
小さなタスクに分割
大きな機能やプロジェクトを小さなタスクに分割します。これにより、各タスクが管理しやすくなり、進捗状況を把握しやすくなります。
頻繁なリリース
小さなバッチで作業を進めることで、頻繁にリリースすることが可能になります。これにより、ユーザーからのフィードバックを早期に得ることができ、必要な修正を迅速に行えます。
リスクの軽減
小さなバッチで作業を進めることで、問題が発生した場合でも影響範囲が小さくなります。これにより、迅速に問題を特定し、修正することができます。
継続的な改善
小さなバッチでの作業は、継続的な改善を促進します。各リリースごとにフィードバックを受け取り、次のリリースに反映させることで、製品の品質を向上させることができます。
継続的な統合
継続的インテグレーション(CI)は、ソフトウェア開発において、開発者が書いたコードを頻繁にメインのコードベースに統合するプロセスです。これにより、コードの変更が小さな単位で行われ、問題の早期発見と修正が可能になります。
頻繁な統合
開発者は自分のコード変更を頻繁に(通常は1日1回以上)リポジトリにマージします。これにより、統合の際に発生する競合やバグを早期に発見できます。
自動化されたビルドとテスト
CIでは、コードがリポジトリにマージされるたびに自動的にビルドとテストが実行されます。これにより、コードの品質を保ちつつ、バグの早期発見が可能になります。
フィードバックの迅速化
自動化されたプロセスにより、問題が発生した場合にはすぐに開発者にフィードバックが返されます。これにより、迅速な対応が可能になります。
リリースサイクルの短縮
CIを導入することで、リリースサイクルが短縮され、頻繁に新しいバージョンをリリースすることが可能になります。これにより、ユーザーからのフィードバックを早期に得ることができます。
協力しあう
協力しあうプラクティスには、チームメンバーが共同で作業を進めるためのさまざまな方法があります。
ペアプログラミング
ペアプログラミングは、二人の開発者が一緒にコードを書く手法です。一人が「ドライバー」として実際にコードを書き、もう一人が「ナビゲーター」としてコードをレビューし、提案を行います。この方法は、以下のようなメリットがあります
コード品質の向上
ナビゲーターがリアルタイムでコードをチェックするため、バグやミスが減少します。
知識の共有
異なる視点やスキルを持つ二人が協力することで、互いの知識や技術が向上します。
迅速な問題解決
二人で問題に取り組むため、解決が早くなります。
CLEANコード
凝集性
関連する機能やデータを一つのモジュールやクラスにまとめること。
例: ユーザー認証に関する機能を一つのクラスにまとめることで、コードの理解と保守が容易になります。
疎結合
モジュールやクラス間の依存関係を最小限にすること。
例: インターフェースを使用して、異なるモジュール間の依存を減らし、変更の影響を最小限に抑えます。
カプセル化
データやメソッドをクラス内に隠蔽し、外部から直接アクセスできないようにすること。
例: クラスのプロパティをプライベートにし、必要な場合のみ公開メソッドを通じてアクセスさせます。
断定的
コードが明確で一貫性があり、意図がはっきりしていること。
例: メソッド名や変数名を意味のあるものにし、コメントを適切に追加してコードの意図を明確にします。
非冗長
重複したコードを避け、シンプルで効率的なコードを書くこと。
例: 共通の処理をメソッドにまとめ、再利用することでコードの重複を減らします。
まずテストを書く
コードを書く前にテストを作成することも役に立ちます。例えば、新しい機能を追加する前に、その機能が正しく動作するかどうかを確認するためのテストケースを作成します。
利点
バグの早期発見
コードを書く前にテストを作成することで、バグを早期に発見しやすくなります。
リファクタリングの安心感
テストがあることで、コードのリファクタリング(改善)を行う際に、既存の機能が壊れていないかを確認できます。
ドキュメントとしての役割
テストケースは、コードの仕様や期待される動作を示すドキュメントとしても機能します。
実践方法
小さなステップで進める
一度に大きなテストを書くのではなく、小さな機能ごとにテストを作成し、段階的に進めます。
ふるまい駆動開発
テストを通じてシステムのふるまいを明確にする手法です。BDDでは、テストケースをユーザーの視点から記述し、システムがどのように動作するべきかを示します。例えば、「ユーザーがログインボタンをクリックしたとき、正しい資格情報が入力されていれば、ダッシュボードページにリダイレクトされるべき」というテストケースを作成します。
利点
明確な仕様
テストケースがシステムの仕様を明確に示すため、開発者間のコミュニケーションが円滑になります。
バグの早期発見
ふるまいを明示することで、期待される動作と実際の動作のギャップを早期に発見できます。
実践方法
Given-When-Then形式
テストケースを「前提条件 (Given)」「操作 (When)」「期待される結果 (Then)」の形式で記述します。
設計は最後に行う
最初から詳細な設計を行うのではなく、実際の開発プロセスを通じて設計を進化させることがプラクティスに挙げられてます。例えば、プロジェクトの初期段階では大まかな設計に留め、実際のコーディングやテストを通じて設計を改善していきます。
利点
適応性の向上
要件や技術的な変更に柔軟に対応できるようになります。
実際のニーズに基づく設計
実際の使用状況やフィードバックを反映した設計が可能になります。
実践方法
反復的なアプローチ
開発サイクルごとに設計を見直し、必要に応じて改善を行います。
テスト駆動開発 (TDD)
テストを先に書くことで、設計が自然と進化し、必要な機能に焦点を当てた設計が行われます。
レガシーコードをリファクタリングする
1. ピンニングテスト
ピンニングテストは、既存のコードの動作を固定するためのテストです。これにより、リファクタリング中にコードの動作が変わらないことを確認できます。まず、現在の動作をテストでキャプチャし、その後の変更が意図しない影響を与えないようにします。
2. 依存性の注入
依存性の注入は、コードの依存関係を外部から注入することで、テストやメンテナンスを容易にする手法です。これにより、モジュール間の結合度が低くなり、個々のコンポーネントを独立してテストできるようになります。
3. ストラングラーパターンと抽象化ブランチ
ストラングラーパターンは、古いシステムを段階的に新しいシステムに置き換える方法です。まず、新しい機能を新しいシステムに実装し、徐々に古いシステムの機能を移行します。抽象化ブランチは、コードの一部を抽象化して新しい実装に切り替えるための技術です。これにより、リファクタリングの影響を最小限に抑えながら、段階的に改善を進めることができます。
4. オープンクローズド原則に基づくリファクタリング
オープンクローズド原則は、ソフトウェアエンティティ(クラス、モジュール、関数など)は拡張に対して開かれており、修正に対して閉じているべきという原則です。これを実現するためには、既存のコードを変更せずに新しい機能を追加できるように設計します。例えば、インターフェースを使用して新しい機能を追加することで、既存のコードに影響を与えずに拡張できます。
これらのプラクティスを実践することで、以下のような価値を得ることができます
コードの信頼性向上
ピンニングテストや依存性の注入により、コードの動作が安定し、バグの発生を防ぎます。
メンテナンス性の向上
依存性の注入やオープンクローズド原則に基づく設計により、コードの変更が容易になります。
段階的な移行
ストラングラーパターンと抽象化ブランチを使用することで、システム全体を一度に変更するリスクを避け、段階的に改善を進めることができます。