内部ループについて

完了

内部ループは、開発者の生産性とフィードバック サイクルに大きな影響を与えるソフトウェア開発の基本的な概念です。 内部ループを理解して最適化することは、効率的な DevOps プラクティスと継続的な改善のために不可欠です。

内部ループとは

内部ループは、コードの記述、ビルド、デバッグ時に開発者が実行する反復プロセスです。 これは、コードがチームと共有されるか、運用環境にデプロイされる前に、開発者のコンピューターでローカルで行われる 迅速なフィードバック サイクル を表します。

主な特性

  • ローカル実行: 開発者のワークステーションで完全に実行されます
  • 高速反復: 迅速なフィードバックと迅速な変更のために設計されています
  • 頻繁な繰り返し: アクティブな開発中に 1 日に何度も実行されました
  • 個々のフォーカス: 単一開発者の生産性に最適化
  • コミット前アクティビティ: コードがバージョン 管理に入る前に発生します

多くの開発チームは、フィードバックが速いほど生産性とコード品質が向上するため、内部ループを できるだけ短く しておきたいものとして認識しています。

コードとビルドの内部ループと中間の内部ループを示すダイアグラム。

テクノロジ別の内部ループのバリエーション

開発者の内部ループ内の 特定のアクティビティ は、次に大きく依存します。

  • 使用されるテクノロジ: プログラミング言語、フレームワーク、ランタイム環境
  • 使用可能なツール: IDE、ビルド システム、およびテスト フレームワーク
  • 開発者設定: 個々のワークフローの最適化と習慣
  • プロジェクトの種類: Web アプリケーション、ライブラリ、マイクロサービス、またはモバイル アプリ

例: ライブラリ開発の内部ループ

ライブラリ開発の場合、一般的な内部ループには次のものが含まれます。

  1. コーディング: ライブラリ コードを記述または変更する
  2. 建物: ライブラリをコンパイルする
  3. テスティング: 単体テストを実行して機能を確認する
  4. デバッグ: テスト中に検出された問題を修正する
  5. コミット: ローカル Git リポジトリに変更を保存する

例: Web フロントエンド開発の内部ループ

Web フロントエンドの作業では、内部ループは異なる方法で最適化されます。

  1. コーディング: HTML、CSS、JavaScript を編集する
  2. バンドル: ビルド ツール (Webpack、Vite など) を実行する
  3. リフレッシュ: ブラウザーを再読み込みして変更を確認する
  4. デバッグ: ブラウザー DevTools を使用して動作を検査する
  5. コミット: ローカル Git リポジトリに変更を保存する

コード、ビルド、テストなどの、さまざまな内部ループを示すダイアグラム。

コンテキスト切り替え

ほとんどの 最新のコードベースは 複数のコンポーネントで構成されているため、開発者の内部ループは、作業内容に応じて 代替 される場合があります。

  • バックエンド API: コード、ビルド、テスト、デバッグに重点を置く
  • フロントエンド UI: コード、バンドル、更新、検査に重点を置く
  • データベース スキーマ: 移行、テスト、ロールバックに重点を置く
  • インフラ: 構成、デプロイ、検証に重点を置く

内部ループ アクティビティの分類

内部ループ内のステップは、 次の 3 つの広範なアクティビティ カテゴリにグループ化できます。

1. 実験

顧客価値を追加するアクティビティ:

  • コーディング: 新機能の記述またはバグの修正
  • 設計: アーキテクチャまたはユーザー インターフェイスの計画
  • プロトタイピング: 新しいアプローチまたはソリューションの探索

特性: これらのアクティビティは、最終製品に直接価値を追加する 唯一 のアクティビティです。

2. フィードバック収集

品質を検証するアクティビティ:

  • 建物: 構文と依存関係を確認するためのコードのコンパイル
  • テスティング: 単体テストを実行して機能を検証する
  • デバッグ: 問題の特定と修正
  • コード分析: リンターと静的アナライザーの実行

特性: これらのアクティビティ は価値を直接追加するのではなく 、コードの品質と正確性を確保するために 不可欠なフィードバック を提供します。

3. 税金

必要であるが、価値やフィードバックを追加しないアクティビティ:

  • コミット: コードをバージョン 管理に保存する
  • 構成: ビルド環境の設定
  • 同期: リモートリポジトリから最新の変更を取得
  • ドキュメントの更新: README ファイルまたはコメントの更新

特性: これらのアクティビティは 必要な作業 ですが、顧客価値を高め、フィードバックを提供することもありません。 アクティビティが 不要な場合は無駄であり、削除する必要があります。

ループの理解に役立つコード、ビルド、テスト、コミットを示すダイアグラム。

分類の例: ライブラリ開発

ライブラリ開発シナリオの場合:

アクティビティ カテゴリ Purpose
Coding 実験 顧客価値を追加する
ビルド フィードバック収集 コードのコンパイルを確認します
テスト/デバッグ フィードバック収集 機能を検証します
コミット 税金 必須ですが、値を追加しません

税カテゴリに コミット することは厳しいように見えるかもしれませんが、分類は、絶対に必要になるまで 最小化 または 延期 する必要があるアクティビティを特定するのに役立ちます。

内部ループの最適化

ループ内のステップを分類したので、 最適化の原則を確立できるようになりました。

コア最適化の原則

1. 速度は変化に比例する

  • ゴール: ループを可能な限り高速に実行する
  • 原理: 合計実行時間は、加えられた変更のサイズに比例する必要があります
  • 益: 小さな変更は、迅速なフィードバックを得ます。大きな変更には適切に時間がかかります

2. フィードバック品質を最大化し、フィードバック時間を最小限に抑える

  • ゴール: 最も有用な情報を最短で取得する
  • 原理: 包括的なテストと迅速な反復のバランス
  • 益: 重大な問題を迅速にキャッチしながら、重要度の低いチェックを延期する

3. 税金の最小化または延期

  • ゴール: 不要なオーバーヘッドを削減する
  • 原理: 無駄をなくし、重要でない活動を延期する
  • 例: ドキュメントの更新をコミット時刻まで延期する

4. 複雑な成長に対処する

  • 挑戦: コードベースが大きくなると、内部ループの速度が自然に低下します
  • 理由: コードが多いほど、テスト、依存関係、ビルド時間が増える
  • インパクト: 小さな変更であっても、フィードバック収集時間が不均衡である

モノリシック コードベースの問題

大規模なモノリシック コードベースでは、次の状況が発生する可能性があります。

  • 小さな変更: 1 つの関数を変更する
  • 不均衡なコスト: 完全なビルドとテスト スイートが完了するまで 10 分以上待ちます
  • 開発者のフラストレーション: フィードバック サイクルが遅くなるにつれて生産性が急落する
  • コンテキストの切り替え: 開発者はビルドを待ってフォーカスを失う

これは、事前に対処する必要がある問題です。

大規模なコードベース最適化の戦略

Teams では、複数の戦略を採用して、大規模なコードベースの内部ループを最適化できます。

1. 増分ビルドとテスト

変更された内容のみをビルドしてテストします。

  • スマート ビルド システム: 変更されたファイルを検出し、影響を受けるコンポーネントのみを再構築する
  • テストの選択: コード変更の影響を受けるテストのみを実行する
  • 依存関係の追跡: どのテストがどのコードに依存するかを理解する
  • ツール: スマート インクリメンタル ビルドで Bazel、Buck、Gradle などのビルド システムを使用する

メリット:

  • 小さな変更のビルド時間を大幅に短縮
  • サイズを変更するための比例フィードバック時間
  • 反復サイクルの高速化

2. 中間結果のキャッシュ

ビルド成果物をキャッシュして、完全なビルドを高速化します。

  • ローカル キャッシュ: コンパイル済みオブジェクトとテスト結果をローカルに格納する
  • 分散キャッシュ: チーム メンバー間でビルド 成果物を共有する
  • リモート実行: クラウド ビルド ファームにコンパイルをオフロードする
  • ツール: ccache、sccache、クラウドベースのソリューションなどのシステムでキャッシュを実装する

メリット:

  • 変更されていないコードの冗長なコンパイルを回避する
  • ブランチスイッチ後にクリーンビルドが速くなる
  • CI/CD パイプラインの実行時間の短縮

3. モジュール化とバイナリ共有

コードベースを小さな単位に分割し、バイナリを共有します。

  • ライブラリの抽出: 一般的な機能を別のパッケージにプルする
  • 境界を定義します。 明確なモジュール インターフェイスと依存関係を作成する
  • バージョン パッケージ: 内部ライブラリの安定したバージョンを発行する
  • 依存関係の管理: パッケージ マネージャーを使用して安定したバージョンを使用する

注意: この戦略は、間違って行われた場合は 両刃の剣 にすることができます(後述の「もつれループ」セクションを参照)。

正しく行われた場合の利点:

  • 小さいコンパイル 単位
  • 独立したバージョン管理とデプロイ
  • アーキテクチャの境界を明確に
  • プロジェクト間で再利用可能なコンポーネント

間違って行われた場合のリスク:

  • 複数のリポジトリ間で変更を必要とする依存関係が絡み合う
  • 外部ループのオーバーヘッドによる税の増加
  • バージョンの不一致の問題

もつれたループの理解

もつれたループの概念は、モジュール化が正しく行われず、内側と外側のループが絡み合う原因となった場合に何が起こるかを示しています。

外側のループ

もつれたループを理解する前に、 外側のループを定義する必要があります。

外側のループ特性:

  • チームコラボレーション: コードは pull request を通じてチームと共有されます
  • 品質ゲート: コード レビュー、自動スキャン、セキュリティ チェック
  • 統合: コードがメイン ブランチにマージされ、デプロイされる
  • より高い税金: コラボレーションと自動化によるオーバーヘッドの増加
  • フィードバックが遅い: 数秒から数分ではなく数分から数時間の単位

外部ループでのコード、ビルド、テスト、コミットを示すダイアグラム。

モジュール化シナリオ

この一般的なシナリオを考えてみましょう。

初期状態: 重い持ち上げを行う アプリケーション固有のフレームワーク を備えたモノリシック アプリケーション。

モジュール化の決定: フレームワークを 別のパッケージに抽出します。

実装手順:

  1. コードを別のリポジトリにプルします。 フレームワーク コードが独自のリポジトリに移動する
  2. CI/CD パイプラインを設定します。 フレームワーク パッケージの自動ビルドと発行
  3. 品質ゲートを追加する: Pull request レビュー、セキュリティ スキャン、承認ワークフロー
  4. パッケージとして発行する: フレームワークがバージョン管理された依存関係になる

最初の結果: 最初はうまく機能します。 モノリスは、安定したフレームワーク バージョンを使用します。

もつれが発生した場合

問題のシナリオ: フレームワークで広範な 新機能 を必要とする 新機能 を開発する必要があります。

問題のポイント: ここで、2 つの異なるリポジトリ内のコードを、それらの間にバイナリ依存関係を持つ 共進化 させる必要があります。

何が起こるか:

  1. フレームワークにメソッドを追加します。 フレームワーク リポジトリで新しい機能を作成する
  2. 外側のループを通過する: コード レビュー、テスト、セキュリティ スキャン、承認
  3. パッケージの発行を待ちます。 フレームワーク パッケージをビルドして発行する必要がある
  4. アプリケーションの更新: 新しいフレームワーク メソッドを使用するようにアプリケーションを変更する
  5. 繰り返す: すべてのイテレーションには、完全な外側のループ サイクルが必要です

問題を: 元のコードベースの 内部ループ に、フレームワーク コード の外側のループが含まれる ようになりました。

もつれたループを参照するコード、ビルド、テスト、コミット、アプリのループを示すダイアグラム。

外側ループ税

外側のループには、大きな税金が含まれています

  • コード レビュー: レビュー担当者がフィードバックを提供するのを待つ
  • セキュリティ スキャン: 脆弱性とコンプライアンスの自動チェック
  • バイナリ署名: 発行済みパッケージの証明書ベースの署名
  • リリース パイプライン: デプロイの自動化とテスト
  • 承認ゲート: 実稼働リリースの手動承認

インパクト: クラスにメソッドを追加してすぐに使用するたびに 、この税金を支払 う必要はありません。

開発者の回避策

通常、次の処理が行われます。

ローカル ハック: 開発者は、内部ループを結合するための回避策を作成します。

  • ローカル パッケージ参照: 発行されたパッケージの代わりにローカル ファイルシステムをポイントする
  • Git サブモジュール: フレームワーク ソースをアプリケーションに直接含める
  • シンボリック リンク: リポジトリ間のリンクを作成する
  • プレリリース パッケージ: テスト フィードに発行する

結果: これらの回避策はすぐに 乱雑 になり、最終的には外側のループ税を支払う必要があります。

モジュール化する正しい方法

モジュール化は本質的に悪くはありません

適切なモジュール化:

  • 安定したインターフェイス: フレームワーク API の変更頻度が低い
  • 独立した進化: フレームワークとアプリケーションが個別に進化する
  • 境界をクリアする: 明確に定義された責任と契約
  • 疎結合: コンポーネント間の最小依存関係

不適切なモジュール化:

  • 密結合: フレームワークとアプリケーションを一緒に変更する必要がある
  • 頻繁な共進化: すべての機能にフレームワークの変更が必要
  • 不明確な境界: コンポーネント間の責任の重複
  • 人工分離: 技術的な理由ではなく、組織のために行われた分割

主な原則: 組織構造ではなく、実際のアーキテクチャ境界に基づいてモジュール化の分割を慎重に行います。

内部ループ最適化のベスト プラクティス

監視と測定

内部ループ メトリックを追跡する:

  • ビルド時間: コンパイルにはどのくらいの時間がかかりますか?
  • テストの実行時間: テストはどのくらいの期間実行されますか?
  • フィードバックの遅延: 保存から結果の表示までの時間
  • 開発者の満足度: 問題点に関する調査チーム

測定ツール:

  • システム分析の構築
  • IDE パフォーマンス プロファイラー
  • テスト実行レポート
  • 開発者の生産性に関するアンケート

積極的にパフォーマンス低下に対処する

警告の兆候:

  • 開発者が低速ビルドについて不平を言う
  • ビルド待機中におけるコンテキスト切り替えの増加
  • チームがローカルでテストのスキップを開始する
  • プルリクエストには「未テスト」のコードが含まれています

対応戦略:

  • 根本原因を直ちに調査する
  • 最適化作業に優先順位を付ける
  • ソリューションにチーム全体を関与する
  • 時間の経過に伴う改善を測定する

トレードオフのバランスをとる

考慮すべき主なトレードオフ:

最適化 Benefit Cost
増分ビルド ローカル ビルドの高速化 複雑なビルド構成
ビルド キャッシュ クリーン ビルドの高速化 ストレージとネットワークのオーバーヘッド
モジュール化 小さいコンパイル 単位 潜在的な絡み合ったループ
テストの数を減らします フィードバックの高速化 信頼度の低下
並列実行 全体的な時間の短縮 リソース使用量の増加

原理: 1 つの側面を改善 すると、多くの場合、 別の問題が発生します。 トレードオフを継続的に評価します。

チームの配置

共同責任:

  • 設計者: テスト性およびモジュール性を考慮した設計
  • 開発者: 効率的なテストを記述し、不要な依存関係を回避する
  • DevOps: ビルド インフラストラクチャとキャッシュを提供する
  • 管理: 内部ループの最適化作業に優先順位を付ける

文化の実践:

  • 内部ループ時間を主要な生産性メトリックとして扱う
  • "低速ビルド" を機能の作業を一時停止する正当な理由にする
  • 内部ループの改善を祝う
  • チーム間で最適化の知識を共有する

重要なポイント

次の原則を覚えておいてください。

  1. 特効薬はありません: 内部ループ最適化のための万能な解決策は存在しません。
  2. 問題を理解します。 速度低下が発生したタイミングとその根本原因を特定する
  3. すべてを測定する: メトリックを追跡して変更の影響を把握する
  4. 積極的に行動する: 生産性に深刻な影響を与える前に問題に対処する
  5. バランスのトレードオフ: すべての最適化にはコストがあります。賢明に選択する
  6. モジュール化を慎重に行う: 利便性ではなく、技術的な境界に基づいてコードベースを分割する
  7. 継続的な改善: 内部ループの最適化は進行中の作業です

アーキテクチャに関する事項: アプリケーションの 構築テストデバッグ 方法に関する決定は、開発者の生産性と幸福度に大きく影響します。 これらの基礎を正しく理解することに時間を費やしてください。