名前を知ることは見つけることではない
あなたは今7つのMOADパターンを知っています。パターン名を知ることは重要です: 見たときにパターンを認識することができます。しかし、制御された教訓での認識と、初めてのコードベースでの検出は異なります。
コードベースは自分の欠陥にラベルを付けません。堆積型MOADは「// O(N²) — 修正してください」というコメント付きではありません。雷鳴の群れはキャッシュミススタンピードとして自身を表明しません。特定の質問を念頭に置いてコードを読むことで、それらを見つけます: どのデータ構造がこれらの値を保持し、ループ内でその値に対してどの操作が実行されますか?
検出は認識とは別のスキルです。 認識は言います: はい、そのパターンはMOAD-0001です。検出は言います: このコードベース内でそのパターンが存在する可能性があるすべての場所を見つけてください、完全なコードが見えるかどうかに関わらず。
初回スキャン
最初のパスはgrepを使用します。各MOADには基質があります: 特定の操作の近くにあると調査価値のある信号となるデータ構造またはAPI。
MOAD-0001 (堆積型): ループ内の List.contains
# 信号: ループ内のリスト変数に対するメンバーシップテスト
grep -rn '.contains(' src/ | grep -v HashSet | grep -v TreeSet
grep -rn 'visited =' src/ | grep -v set | grep -v Set
MOAD-0002 (インターテングル): フェーズ間の共有可変フラグ
# 信号: 1つのサブシステムで書かれ、別のサブシステムで読まれる静的可変フィールド
grep -rn 'static ' src/ | grep -v final | grep -v class | grep -v void
MOAD-0003 (漏洩コンテキスト): プール化されたエグゼキューター内のThreadLocal
# 信号: ThreadLocal.set() は ThreadLocal.remove() を保証しなくて実行
grep -rn 'ThreadLocal' src/
grep -rn 'ThreadLocal.set' src/ -l
MOAD-0004 (ログされた秘密): ログ出力内のHTTPヘッダー
# 信号: 認証エンドポイント付近でのヘッダー変数とのログ呼び出し
grep -rn 'log.*header' src/
grep -rn 'Authorization' src/ --include='*.log'
MOAD-0005 (雷鳴の群れ): 同期なしのキャッシュミス
# 信号: cache.get() + null チェック + ロックなしの cache.put()
grep -rn 'cache.get' src/ -A4 | grep 'cache.put'
これらのパターンは候補を生成し、確認されたものではありません。すべての候補には分類が必要です: 周囲のコードを読み、データ構造の型を確認し、操作がスケールで実行されることを確認します。
複雑性の読み取り
Grepは候補を見つけます。読み取りは確認します。候補ファイルを開くと、1つの質問でコードを読みます: この操作のコストは入力サイズとともに増加しますか?
MOAD-0001の確認プロトコル:
1. 外側のループを見つけます。その反復カウントは何を制限していますか?
2. 内側の操作を見つけます(.contains、.indexOf、'in')。それは何のデータ構造に対して実行されますか?
3. そのデータ構造は、外側のループを駆動するのと同じ入力とともに増加しますか?
4. はい: コストはO(N²)ここで N = 入力サイズ。確認された欠陥。
5. いいえ: 内側の構造は制限されています(設定、列挙値、小さな定数)。誤検知。
Nノードを訪問するグラフトラバーサル、各ステップで visited リストをチェック: ループと内側のデータ構造の両方がNとともに増加します。確認。
リクエストハンドラー、5人の管理者IPのホワイトリストをチェック: ホワイトリストはリクエストボリュームとともに増加しません。誤検知。
同じプロトコルが各MOADに適用されます: 外側のドライバーを特定し、内側の構造を特定し、両者がスケールとともに増加するかどうかを質問します。
サージスコア: 発見の優先順位付け
すべての確認された欠陥がすぐにパッチを必要とするわけではありません。10,000のダウンストリーム依存物があるライブラリのMOADは、プライベートな内部ツール内の同じMOADより高いサージスコアを持っています。
サージスコア = スピードアップ × イン度。 スピードアップ: 典型的な本番スケールでの修正の実行速度はどのくらい速いですか? イン度: アップストリームがマージするときに自動的に修正を継承するダウンストリームパッケージまたはサービスはいくつありますか?
50,000ノードのグラフで実行されるApache Mavenの依存性リゾルバーの確認されたMOAD-0001、1,000以上のダウンストリームMavenプラグインが変更を自動的に継承: サージスコアは非常に高いです。この修正はキューの前面に属します。
依存物がない単一ユーザーのCLIツール内の確認されたMOAD-0001: サージスコアはほぼゼロです。修正する価値がありますが、緊急ではありません。
ワーカホリック vs 大食らいノード。 高いベットウィーンネス & 高いスピードアップを持つノードはワーカホリックです: 重要なフローを処理 & ブロックを解除するとダウンストリームキューをフラッシュします。ダウンストリーム容量を確認した後でのみパッチします。高い出度 & 低いスピードアップを持つノードは大食らいです: 供給されているすべてを消費して痛みを感じません。ワーカホリックをダウンストリーム容量をステージングすることなくパッチすると、インフラストラクチャスケールでMOAD-0005(雷鳴の群れ)を作成します。
スキャンからマージまで: MOADパイプライン
確認された高いサージスコアを持つ欠陥はパイプラインを通ります。各ステージは成果物を生成します。ステージは任意ではありません。
scan → 候補リスト(grep出力、静的分析結果)
ticket → 欠陥説明(MOAD番号、場所、複雑性分析)
patch → コード変更(データ構造スワップ、プリミティブ採用)
test → ユニットテスト(O(1)証明: N=100でN=10,000修正をタイミング)
UNDF → 公開開示投稿(undefect.com、パブリックドメイン)
disclose → CVE または CWE 参照(セキュリティ関連の場合)
PR → アップストリーム プル要求(パッチ + テスト + UNDF リンク)
merge → メンテナー受け入れ; 修正がバージョン上昇を通じて伝播
各成果物は次のステージに供給します。テストなしのパッチは検証できません。開示なしのテストは他のインスタンスへの修正の伝播を妨げます。アップストリームPRなしの開示はフォークに修正を取り残します。
MOADポスト(UNDF)はほとんどのエンジニアが省くステージです。 欠陥を修正し、PRを送信し、完了と見なします。しかし、名前付きポストなしの修正は、同じパターンが将来のエンジニアに会うたびに問題と修正の両方を独立して再発見しなければならないことを意味します。MOADポストは知識ループを閉じます: パターン名を付け、検出方法を示し、& パッチにリンクします。将来の研究者はパターン名を検索して修正を見つけます。
スケールで惑星をパッチします。 広く使用されているライブラリのMOAD-0001修正は、それをインポートするすべてのプロジェクトに伝播します。MOADポストは、そのライブラリをアップグレードする可能性のないプロジェクトのエンジニアが修正を学習するようにしています。両方のパスは並行して実行されます。
欠陥チケットの作成
良い欠陥チケットは5つの質問に答えます:
1. どこ: 正確なファイル、クラス、関数、および行範囲
2. 何: データ構造型と、それに対する操作
3. なぜ: 複雑性分析(O(N²)またはさらに悪い、Nが定義される)
4. 影響: 最悪のケースの動作をトリガーするとは何か、どのスケールで
5. 修正: 置き換えるデータ構造またはプリミティブ
すべての5つに答えるチケットは自己完結しています: あなたの分析を読んだことのないメンテナーはあなたの発見を再現してあなたの修正を検証できます。(3)または(4)をスキップするチケットはメンテナーがマージする前にあなたの複雑性分析を繰り返す必要があります。そのきしみはマージの確率を減らします。
信頼性は複合です。 明確なチケット、よく対象のパッチ、& ベンチマークテストを含む最初のPRはマージされます。同じ著者からの2番目のPRはより少ないきしみでレビューされます。3番目のPRは最初の2つをマージしたメンテナーによってレビューされます。オープンソースの評判は成果物の台帳です: 受け入れられた各パッチは次の信頼を獲得します。
実際の候補を読む
Pythonの実際のMOAD-0001候補は以下の通りです。それを読んでトリアージプロトコルを完了してください。
class DependencyResolver:
def resolve(self, package, resolved=None, seen=None):
if resolved is None:
resolved = []
if seen is None:
seen = []
if package in seen:
return
seen.append(package)
for dep in self.registry.get_dependencies(package):
self.resolve(dep, resolved, seen)
resolved.append(package)
return resolved
トリアージ質問:
1. `seen` は何のデータ構造ですか?
2. 行6で何のプロセスが対して実行されますか?
3. `seen` は入力サイズとともに増加しますか?
4. 再帰呼び出しを駆動するループは入力サイズとともに増加しますか?
5. これは確認されたMOAD-0001または誤検知ですか?
あなたのパッチ
高いサージスコアを持つ確認された欠陥には、完全なパッチが必要です: コード修正、改善を証明するテスト、& MOADポスト概要。
テストは正確性テストではなく、パフォーマンステストである必要があります。 正確性テストは修正の前後にパスします — これはポイントです。出力は変更されません。パフォーマンステストは2つの入力サイズで改善を証明します:
import time
def build_graph(n):
# n パッケージ、それぞれが前のパッケージに依存する
return {f'pkg{i}': [f'pkg{i-1}'] if i > 0 else [] for i in range(n)}
for n in [100, 1000, 5000]:
registry = build_graph(n)
resolver = DependencyResolver(registry)
start = time.perf_counter()
resolver.resolve(f'pkg{n-1}')
elapsed = time.perf_counter() - start
print(f'n={n}: {elapsed:.4f}s')
修正の前に、経過時間はnで二次的に増加します。修正後、直線的に増加します。両方を印刷してPR説明に数字を含めます。
MOADポスト概要は: パターン名、基質(Python依存性リゾルバー)、検出方法(seenが[]で始まる場所でin seenを探す)、修正、& PRへのリンク。投稿はundefect.comに公開ドメインとして表示されます。「Pythonループの遅いリスト メンバーシップ」を検索する将来のエンジニアはそれを見つけます。