Slide 1

Slide 1 text

良いソフトウェアと コードレビュー 阿川 耕司 / Koji AGAWA

Slide 2

Slide 2 text

小売向けサイネージのプロダクト「ミライネージ」 でテックリードを担当している。 阿川 耕司 / Koji AGAWA 2013年度 中途入社 AI事業本部 協業リテールメディアディビジョン @atty303 @atty303

Slide 3

Slide 3 text

1.良いソフトウェアの基準 2.コードレビュープロセス 3.レビュイーのベストプラクティス 4.レビュアーのベストプラクティス 5.まとめ

Slide 4

Slide 4 text

良いソフトウェアの基準

Slide 5

Slide 5 text

良いソフトウェアの基準 可読性: コードが清潔で、理解しやすい 保守性: 将来の変更や拡張がしやすいコード 効率性: パフォーマンスとリソース使用のバランス 信頼性: エラー処理とバグの少ない設計 拡張性: 将来の拡張に対応しやすい設計

Slide 6

Slide 6 text

可読性

Slide 7

Slide 7 text

可読性とは? ソフトウェアの可読性は、コードがどれだけ清潔で、理解しやすいかという点に関連する。可読性 の高いコードは、他の開発者が容易に理解し、修正や改善がしやすい状態にある。これを実現 するためには、命名規則の一貫性、コードの構造化、適切なコメントの使用などが重要である。 可読性の高いコードは、チームでの協力や長期的なプロジェクトの成功に不可欠である。

Slide 8

Slide 8 text

可読性の要素

Slide 9

Slide 9 text

命名規則の一貫性 #include int d; // グローバル変数 void f(int a) { int b = 5; d = a + b; } int main() { int n = 10; f(n); printf("%d\n", d); return 0; } 悪い例 #include int totalSum; // グローバル変数の使用を避けるべきだ が、明確な名前を使用 void addAndStore(int number) { int baseValue = 5; totalSum = number + baseValue; } int main() { int inputValue = 10; addAndStore(inputValue); printf("Total Sum: %d\n", totalSum); return 0; } 良い例

Slide 10

Slide 10 text

ワンポイント💡 識別子の長さはそのスコープの長さと一致させる • 全て説明的にすれば良いというものでもない • 関数内のスコープの短い変数は1文字で命名しても良い(関数型言語で顕著) • スコープが長くても頻繁に使う関数は短く命名しても良い(テキスト翻訳関数など)

Slide 11

Slide 11 text

コードの構造化 #include int main() { int x=0,y=0,z=0; printf("Enter two numbers:\n"); scanf("%d %d",&x,&y); z=x+y;printf("Sum is: %d\n",z); if(z > 10){ printf("Sum is greater than 10.\n"); }else{ printf("Sum is not greater than 10.\n"); } return 0; } 悪い例 #include int readNumber(const char* prompt) { int num; printf("%s", prompt); scanf("%d", &num); return num; } int calculateSum(int a, int b) { return a + b; } void printResult(int sum) { printf("Sum is: %d\n", sum); if (sum > 10) { printf("Sum is greater than 10.\n"); } else { printf("Sum is not greater than 10.\n"); } } int main() { int x = readNumber("Enter first number: "); int y = readNumber("Enter second number: "); int z = calculateSum(x, y); printResult(z); return 0; } 良い例

Slide 12

Slide 12 text

適切なコメントの使用 #include int main() { // 変数iを宣言して0に初期化 int i = 0; // 10未満の間、ループを繰り返す for(i = 0; i < 10; i++) { // iの値を出力 printf("%d\n", i); } // プログラムの終了 return 0; } 悪い例 #include int main() { // 0から9までの数を出力する for(int i = 0; i < 10; i++) { printf("%d\n", i); } return 0; } 良い例

Slide 13

Slide 13 text

ワンポイント💡 コメントは書かないに越したことはない • Howを説明するコメントは不要。コードを読めばわかる • Whyを説明するコメントが有用。コードから読み取れない、背景情報がわかる

Slide 14

Slide 14 text

リファクタリング #include int main() { int x = 5, y = 10; int sum = x + y; printf("Sum of x and y is: %d\n", sum); int a = 15, b = 20; sum = a + b; printf("Sum of a and b is: %d\n", sum); // さらに別の変数の和を計算し表示する処理が続く... return 0; } 悪い例 #include int calculateSum(int num1, int num2) { return num1 + num2; } void printSum(int num1, int num2) { int sum = calculateSum(num1, num2); printf("Sum of %d and %d is: %d\n", num1, num2, sum); } int main() { printSum(5, 10); printSum(15, 20); // 他の数値の和を計算し表示する処理も簡単に追加できる return 0; } 良い例

Slide 15

Slide 15 text

ワンポイント💡 • リファクタリングと機能追加を一緒にしない • リファクタリングのプルリクエストを投げてから機能追加する

Slide 16

Slide 16 text

ドキュメンテーション #include // 処理を行う関数 void process(int a, int b) { printf("%d\n", a + b); } int main() { int x = 5, y = 10; process(x, y); // xとyを処理する return 0; } 悪い例 #include /** * 二つの整数の和を計算し、結果を表示する関数 * @param a 最初の整数 * @param b 二番目の整数 */ void printSum(int a, int b) { printf("The sum of %d and %d is: %d\n", a, b, a + b); } int main() { int firstNumber = 5, secondNumber = 10; printSum(firstNumber, secondNumber); // 二つの数の和を表 示 return 0; } 良い例

Slide 17

Slide 17 text

ワンポイント💡 アプリケーションとライブラリ • ライブラリを利用してアプリケーションを開発する • アプリケーションを利用するアプリケーションというものはない • ライブラリコードはドキュメント化する • アプリケーションコードはドキュメント化しなくて良い • ただしアプリケーションの仕様ドキュメントは重要

Slide 18

Slide 18 text

オススメの1冊

Slide 19

Slide 19 text

保守性

Slide 20

Slide 20 text

保守性とは? ソフトウェア開発において、プログラムが将来的にどれだけ簡単に変更や拡張ができるかを示す 指標である。保守性の高いソフトウェアは、新しい機能の追加、バグの修正、またはシステムの 更新が比較的簡単に行える。 保守性の高いソフトウェアは以下のメリットがある: • 時間とコストの節約:保守性が高いと、将来的に変更や拡張を行う際にかかる時間とコストが 減る。 • エラーの減少:コードが明確で管理しやすいと、新しいバグを生じにくくなる。 • 柔軟性:市場や技術の変化に迅速に対応できる。

Slide 21

Slide 21 text

保守性の要素

Slide 22

Slide 22 text

モジュール性の高い設計 モジュール性とは、プログラムを小さな部分(モジュール)に分けることである。 それぞれのモジュールは独立して機能し、他の部分とは明確に区分される。 このようにすると、一部のコードを変更しても他の部分に影響を与えにくくなる。

Slide 23

Slide 23 text

例:マイクロサービスアーキテクチャ • 独立性: 各マイクロサービスは他のサービスから独立しており、独自のデータベースとビジネスロジックを持つ ことができる。これにより、サービス間の結合が緩くなり、一つのサービスを変更・更新しても他のサービスに影 響を与えるリスクが低減する。 • スケーラビリティ: 各マイクロサービスは個別にスケールアップまたはスケールダウン可能である。需要が高い サービスだけをスケールすることで、リソースを効率的に使用できる。 • 柔軟な技術スタック: 異なるマイクロサービスは異なる技術スタックで開発されることが可能である。これによ り、最適なツールや言語をサービスごとに選択でき、イノベーションと適応性が向上する。 • 容易なデプロイメントとメンテナンス: 小規模なサービスはデプロイやアップデートが容易で、システム全体を停 止することなくメンテナンスを行える。 • 障害の局所化: 一つのサービスで問題が発生しても、他のサービスには影響が及びにくく、システム全体の安 定性が保たれる。

Slide 24

Slide 24 text

ワンポイント💡 得てしてマイクロサービスはオーバーエンジニアリングになりがち • 特にプロダクト初期の少人数期に採用すると開発速度が遅くなる • モジュラーモノリスという選択肢 • 組織形態にあわせて選択する

Slide 25

Slide 25 text

良いドキュメンテーション ドキュメンテーションとは、ソフトウェアの仕様、設計、使用方法などを記述した文書のこと。 良いドキュメンテーションは、他の開発者がソフトウェアの構造やコードの目的を理解しやすくす る。

Slide 26

Slide 26 text

例:Design Doc ● 概要 ○ プロジェクトの目的や背景。解決しようとしている問題または達成しようとしている目標。 ● スコープ ■ ドキュメントが対象とする範囲。何を含め、何を含めないかについての明確な説明。 ● 既存のシステムとの比較 ■ 既存のシステムや解決策との比較。新しい設計がどのように異なるか、または改善するか。 ● 設計の詳細 ■ アーキテクチャ、データフロー、コンポーネント、インターフェースの詳細。使用する技術やアルゴリズム。 ● リスクとトレードオフ ■ 設計のリスクや潜在的な問題点。トレードオフや妥協点に関する考慮事項。 ● 実装計画 ■ 開発のフェーズやマイルストーン。タスクの割り当てやタイムライン。 ● テスト戦略 ■ システムのテスト方法。パフォーマンス、セキュリティ、ユーザビリティのテスト計画。 ● スケーラビリティとパフォーマンス ■ システムのスケーラビリティに関する計画と戦略。パフォーマンス目標と基準。 ● 保守とサポート ■ システムの長期的な保守計画。サポート体制や文書のアップデート方針。

Slide 27

Slide 27 text

コードのリファクタリング リファクタリングとは、ソフトウェアの外部から見た振る舞いを変えずに、内部の構造を改善する 作業である。 定期的にリファクタリングを行うことで、コードはより読みやすく、管理しやすくなる。

Slide 28

Slide 28 text

オススメの1冊

Slide 29

Slide 29 text

効率性

Slide 30

Slide 30 text

効率性とは? 効率性は、ソフトウェアがパフォーマンスとリソースの使用のバランスをどのように取っているか である。効率的なソフトウェアは、計算資源を最適に利用し、応答時間が短く、ユーザーにとって 高速な体験を提供する。このためには、アルゴリズムの選択、メモリ管理、並行処理の適切な利 用などが重要となる。

Slide 31

Slide 31 text

効率性の要素

Slide 32

Slide 32 text

アルゴリズムの選択 適切なアルゴリズムの選択: 効率性を高めるためには、問題に最適なアルゴリズムを選択する ことが重要である。例えば、ソート処理には、クイックソート、マージソート、ヒープソートなどさま ざまなアルゴリズムがあるが、データの特性や量によって最適なアルゴリズムは異なる。 計算複雑性の理解: アルゴリズムの計算複雑性(ビッグ・オー記法で表される)を理解し、データ 量が増えた時のパフォーマンスの変化を予測することが重要である。

Slide 33

Slide 33 text

メモリ管理 効果的なメモリ使用: 不要なメモリ確保を避け、使用されなくなったメモリは適切に解放すること が重要である。メモリリーク(不要になったメモリが解放されずに残ること)は、長期間の実行で パフォーマンスを低下させる原因になる。 データ構造の最適化: データを扱う際には、適切なデータ構造を選択することが重要である。例 えば、検索が頻繁に行われる場合はハッシュテーブルを、データの順序が重要な場合はリスト や配列を使用するなどである。

Slide 34

Slide 34 text

並行処理の適切な利用 マルチスレッドと非同期処理: 複数のプロセスやスレッドを使用して作業を並行して行うことで、 効率を高めることができる。ただし、並行処理はデッドロックや競合状態などの問題も引き起こす 可能性があるため、慎重な設計が必要である。 スケーラビリティの確保: システムがスケールアウト(水平拡張)やスケールアップ(垂直拡張)を 行った際にも、効率良く動作するよう設計することが重要である。

Slide 35

Slide 35 text

オススメの1冊

Slide 36

Slide 36 text

信頼性

Slide 37

Slide 37 text

信頼性とは? 信頼性はソフトウェア開発において非常に重要な概念である。ソフトウェアの信頼性が高いと は、そのソフトウェアが要求された機能を正確に、期待通りに、そして一貫して実行できることを 意味する。これには、予期せぬ状況やエラーが発生した際にも、ソフトウェアが適切に対応する 能力が含まれる。

Slide 38

Slide 38 text

信頼性の要素

Slide 39

Slide 39 text

エラー処理の戦略 ソフトウェアは、予期しない入力やシステムの障害に対処できるように設計される必要がある。こ れには、例外処理、エラーメッセージの適切な使用、およびリカバリ手順の実装が含まれる。良 いエラー処理は、問題が発生した際にソフトウェアが安全に停止するか、または問題を報告して 適切に対応することを保証する。

Slide 40

Slide 40 text

バグの少ない設計 バグはソフトウェアの信頼性を低下させる。高品質なコードを書くこと、設計段階での厳密なレ ビュー、コーディング規約の遵守などがバグを最小限に抑えるのに役立つ。また、モジュラー設 計(ソフトウェアを小さく、管理しやすい部分に分けること)も、複雑性を減少させ、バグの発生を 減らすのに効果的である。

Slide 41

Slide 41 text

堅牢なテスト戦略 ソフトウェアテストは、エラーやバグを早期に発見し、修正するために不可欠である。これにはユ ニットテスト(個々のコンポーネントのテスト)、統合テスト(複数のコンポーネントの相互作用のテ スト)、システムテスト(ソフトウェア全体のテスト)などが含まれる。また、自動テストの使用は、 一貫性のあるテスト実行と効率の向上をもたらす。

Slide 42

Slide 42 text

オススメの1冊

Slide 43

Slide 43 text

拡張性

Slide 44

Slide 44 text

拡張性とは? 拡張性は、ソフトウェア開発において非常に重要な概念であり、特に長期的なプロジェクトや継 続的に進化するシステムでの重要性が高まる。拡張性の高いソフトウェア設計は、将来の変更 や機能追加を容易にし、ソフトウェアの持続可能性と効率を大幅に向上させる。

Slide 45

Slide 45 text

拡張性の要素

Slide 46

Slide 46 text

抽象化 抽象化は、具体的な実装の詳細を隠蔽し、より高レベルのインターフェースを提供するプロセス である。抽象化により、システムの特定の部分がどのように実装されているかを知らなくても、そ れを使用することができる。 例えば、データベースへのアクセスを抽象化することで、背後のデータベースエンジンが変更さ れても、アプリケーションの他の部分には影響を与えない。

Slide 47

Slide 47 text

モジュール性 モジュール性は、ソフトウェアを独立した、交換可能な部分、つまり「モジュール」に分割すること を意味する。各モジュールは特定の機能や責務を持ち、他のモジュールとは明確に分離されて いる。これにより、新しい機能を追加する際には、新しいモジュールを作成し、既存のシステムに 統合するだけで済む。

Slide 48

Slide 48 text

低結合性 結合性は、システム内の異なるコンポーネント間の相互依存の度合いを示す。低結合性の設計 では、各コンポーネントは他のコンポーネントと最小限の依存関係しか持たず、一部の変更がシ ステム全体に波及することを防ぐ。 例えば、サービス指向アーキテクチャ(SOA)では、異なるサービスが独立して動作し、明確に定 義されたインターフェースを通じてのみ互いに通信する。

Slide 49

Slide 49 text

オススメの1冊

Slide 50

Slide 50 text

コードレビュープロセス

Slide 51

Slide 51 text

コードレビューとは?

Slide 52

Slide 52 text

良いソフトウェアを 維持するための活動

Slide 53

Slide 53 text

バグの早期発見と修正 良いソフトウェアは信頼性が高く、予期しないエラーや問題が少ないものである。コードレビュー により、一人の開発者が見逃すかもしれないバグや論理的な誤りを他のチームメンバーが発見 し、修正することができる。このプロセスは、製品の品質を高め、ユーザーにとって信頼できる体 験を提供する。

Slide 54

Slide 54 text

パフォーマンスの最適化 良いソフトウェアは効率的で高速なパフォーマンスを提供する。コードレビューを通じて、より効 率的なアルゴリズムやコーディング手法が提案され、パフォーマンスの向上に寄与することがあ る。これにより、ソフトウェアはより迅速に動作し、リソースを節約することができる。

Slide 55

Slide 55 text

保守性と可読性の向上 良いソフトウェアは、長期にわたり容易に保守できる。コードレビューは、コードの可読性と保守 性を向上させるのに役立つ。レビューにより、コードはより整理され、文書化され、理解しやすく なる。これにより、将来的な変更や拡張が容易になる。

Slide 56

Slide 56 text

チームのスキル向上と知識共有 良いソフトウェアを開発するためには、チーム全体のスキルが重要である。コードレビューは、経 験豊富な開発者から初心者への知識の伝達や、ベストプラクティスの共有の場となる。これによ り、チーム全体の技術的能力が向上し、より高品質なソフトウェアを生み出すことが可能になる。

Slide 57

Slide 57 text

コーディング標準と一貫性の確保 コードレビューは、チーム全体で一貫したコーディング標準を維持するのにも役立つ。一貫した スタイルと基準により、コードはより整理され、チームメンバー間での理解が容易になる。これ は、ソフトウェアの品質とチームの効率を向上させる要素である。

Slide 58

Slide 58 text

戦略的コードレビュー 戦術的コードレビュー

Slide 59

Slide 59 text

戦略的コードレビュー 戦略的コードレビューは、より大きな視野でコードとその開発プロセスを見ることに焦点を当てる。 • プロジェクトの目標と整合性: コードがプロジェクトの長期的な目標やビジョンに合致しているかどうか を評価する • アーキテクチャとデザイン: システムの全体的な構造やデザインパターンが適切かどうかを検討する • 保守性とスケーラビリティ: 長期的な視点で、コードの保守性や将来の拡張性を評価する • パフォーマンスとセキュリティ: システム全体のパフォーマンスとセキュリティの側面を考慮する • 技術的負債の管理: コード内の技術的負債を特定し、その対処計画を立てる

Slide 60

Slide 60 text

戦術的コードレビュー 戦術的コードレビューは、より具体的な技術的詳細に焦点を当て、個々のコードの品質と機能性 を評価する。 • コーディング規約の遵守: コーディングスタイルと規約が遵守されているかどうかをチェックす る • バグの検出: ロジックエラーや潜在的なバグを特定し、修正する • コードの明瞭さと簡潔さ: コードが明瞭で理解しやすく、無駄がないかを評価する • テストの充実: 十分なユニットテストや統合テストが行われているかを確認する • 機能性と要件の適合: 提出されたコードが機能的に要件を満たしているかを検証する

Slide 61

Slide 61 text

ワンポイント💡 戦略 >>> 戦術 • 戦術的コードレビューは現在でもある程度が生成AIにより行える • 近い将来、ほぼ完全にAIまかせのコードレビューになっていく • 人間の価値は戦略的コードレビューになる

Slide 62

Slide 62 text

レビュイーのベストプラクティ ス

Slide 63

Slide 63 text

明確な説明を提供する • プルリクエストの説明: 変更内容やその理由、特に注目してほしいポイントを詳細に説明する。 • ドキュメントとコメント: コード内に適切なコメントを付け、必要に応じてドキュメントを更新する。

Slide 64

Slide 64 text

コードを分割して提出する • 小規模な変更: 大きな変更よりも小規模で理解しやすい変更を提出する。 • 関連性のある変更: 一つのプルリクエストには、関連性のある変更のみを含める。

Slide 65

Slide 65 text

自己レビューを行う • 自己チェック: 提出前に自分のコードを見直し、明らかなミスやスタイルの不一致がないか確認 する。

Slide 66

Slide 66 text

レビュアーのフィードバックに対応する • オープンな姿勢: フィードバックを受け入れ、防御的にならずに対応する。 • 質問への迅速な回答: レビュアーの疑問には迅速に回答し、必要ならば追加情報を提供す る。

Slide 67

Slide 67 text

テストと検証 • ユニットテスト: 新しいコードや変更されたコードに対して、十分なテストを実施する。 • 統合テスト: アプリケーション全体での動作を確認し、他の部分への影響をチェックする。

Slide 68

Slide 68 text

コードの品質に自信を持つ • ベストプラクティスの適用: クリーンコードの原則やコーディング規約に従って、品質の高いコー ドを書くよう努める。 • 継続的な学習: 新しい技術やベストプラクティスについて学び、自分のコーディングスキルを向 上させる。

Slide 69

Slide 69 text

コミュニケーションを大切にする • 明確なコミュニケーション: レビュアーとのコミュニケーションで明確かつ敬意を持って対応す る。 • 感謝の表明: レビューの時間を割いてくれたレビュアーに感謝を示す。

Slide 70

Slide 70 text

レビュアーのベストプラクティ ス

Slide 71

Slide 71 text

コードを丁寧に読む • 詳細な読み込み: 提出されたコードをじっくりと読み、理解を深める。 • コンテキストの理解: コードの変更がなぜ必要か、背景や目的を理解する。

Slide 72

Slide 72 text

ポジティブなフィードバックを含める • 良い点の指摘: 効果的なコードや良い実践例を見つけたら、積極的に指摘する。 • バランスの取れたレビュー: 改善点だけでなく、良い点も強調する。

Slide 73

Slide 73 text

建設的なフィードバックを提供する • 具体的な提案: 改善のための具体的なアイデアや例を提供する。 • 明確な説明: 指摘する問題点や提案の理由を明確にする。

Slide 74

Slide 74 text

コミュニケーションを大切にする • 敬意を持ったコミュニケーション: 相手に対して尊重を持って接する。 • 開かれた対話: 質問や議論を奨励し、相手の意見を尊重する。

Slide 75

Slide 75 text

タイムリーなフィードバックを提供する • 迅速なレビュー: 提出されたコードを可能な限り迅速にレビューする。 • 遅延の回避: 遅延がチームの生産性に影響を与えないようにする。

Slide 76

Slide 76 text

まとめ

Slide 77

Slide 77 text

まとめ • 良いソフトウェアの定義を紹介 • コードレビューは良いソフトウェアを維持するための活動 • コードレビューは人間同士のコミュニケーション、お互いにリスペクトしよう

Slide 78

Slide 78 text

ありがとうございました