デスロップ:生成コードを入念な人間のコードと区別がつかなくする は何をしますか?
スロップはスタイルの問題ではなく、信頼の問題です。スロップを見つけたレビュアーはコードを読むのをやめ、さらに探し始めます。修正は決して表面的なものだけでは不十分です。以下の各パスは見た目だけでなく、必要性も検証します。
実際のdiffから作業し、自分が書いたものの記憶から決して作業しないでください。作業が複数のブランチにまたがる場合は、各ブランチのdiffをマージベースと照合してください。
パス0 — 小さく作成し、各行を正当化する(予防は事後修正より優れている)
スロップは存在する前に取り除くのが最も安上がりです。構築時には:
- 少しずつ構築する。 一つのことを行う最小の変更から始め、次の部分を追加する前にそれを正しく慣用的にします。一度に書かれた大きなdiffは常にスロップを蓄積しますが、このスキルに照らしてレビューされたN個の小さな増分は蓄積しません。これにより、レビュアーが一貫して信頼し、1つの大きなPRよりも早く承認する小さなPRが生成されます。
- 作成時にすべての行を問いただす:この行はなぜ存在するのか?なぜこれが既存の関数の呼び出しではなく新しい関数なのか?なぜ新しいファイルなのか?答えが「生成がそう流れたから」なら、削除するか統合してください。すべての追加には、レビューで声に出して述べられる理由が必要です。
- 複雑にしすぎない。 2番目の呼び出し元が現れるまでは、明白な5行バージョンが抽象化された25行バージョンに勝ります。推測的なパラメータも、「将来の柔軟性のため」も、1つの値のラッパー型も不要です。
- 車輪の再発明をしない — クリーンアップ時だけでなく、作成時にも:ユーティリティを書く前に、リポジトリ内で既存のヘルパーをgrepし、その問題を担当するパッケージが依存関係ツリーにあるか確認してください(パス2参照)。
- 書く前に近隣に合わせる。 最初に隣接するファイルを読み、その命名、エラー処理、テストハーネスを採用し、新しいコードが常にそこにあったかのように読めるようにします。
タスクを少しずつ構築できない場合(大規模な移植、ベンダー契約など)、その旨を伝え、誰かが見る前に、より厳格なパス1〜5の監査で補償してください。
パス1 — コメント
最大の兆候。1つのルールを適用します:コメントはコードが示せない制約を記述しなければなりません。 それ以外はすべて削除します。
即時削除対象:
- ナレーション:
// エントリをループする、// ヘルパーを呼び出す、// 結果を返す - コードや自身のドキュメントヘッダーを本文で再度言い換えるもの
- レビュアー向けの説明:
// これは上で確認したので安全、// 注意...— それはあなたがdiffを擁護しているのであって、次の読者に情報を伝えているわけではありません - PR、レビュー、会話、または「最近変更された」への言及
- 変更が正しい理由を説明するコメント(PR本文に属する)
- 約300行未満のファイルでのセクションバナー(
// ---- ヘルパー ----)
保持するもの(これらは重要な役割を果たします):
- ワイヤーフォーマットやプロトコルの制約、特に公開仕様のないリバースエンジニアリングされたもの:フィールドXはnullでも存在しなければならない;クライアントYはZがないとペイロード全体を拒否する;上流のソースファイルを引用すること
- なぜそうしないのかのコメント:なぜ明白なアプローチが却下されたのか、何が壊れるのか
- 読者がつまずくプラットフォームの事実:このenv変数はUnixの慣例である;このAPIはWindowsでは静かに何もしない
- セキュリティ/データ安全の不変条件:生のレコードをキャッシュしてはいけない、シークレットが埋め込まれている
- 上流のデフォルトに反する意図的なポリシーの固定と、その解除条件
密度のヒューリスティック(ルールではありません):グルー/CRUDコードの場合、追加行15行あたり約1コメント行を超えると再確認が必要です。 undocumentedプロトコルコードの場合、高密度は正しい場合があります — コメント自体が仕様です。各コメントを保持/削除リストで判断し、数だけで判断しないでください。
ドキュメントヘッダー:純粋な制約のみの4〜8行。ヘッダーがモジュールの動作を段階的に説明する段落になっている場合は、ファイル名とエクスポートが既に伝えていないことだけに削減してください。
パス2 — 依存関係やstdlibが既に提供しているものを自前で実装しているコード
2番目に大きい兆候:uuid、ハッシュ化、semver解析、globマッチング、日付計算、ディープマージの再実装。
手順:
- パッケージが既に依存しているものを確認する(package.jsonを読み、記憶に頼らない)。新しいものを追加するより、既存の依存関係またはランタイムstdlibを優先する。
- 新しい依存関係が正当化される場合、退屈で実績のあるものを選ぶ。
- 既存の自前実装を置き換える前に、経験的に同等性を証明する — 同じ入力に対して両方を実行し、出力を比較する。決定論的IDやハッシュコードは特に:出力を変更する置き換えは、既存ユーザーの冪等性を静かに壊す。
- 自前実装を保持することが本当に正しい場合(10行のために依存関係を避ける)、レビュアーの質問を予想し、その議論が依存関係よりも価値があるかを判断する。通常はそうではない:退屈なものがレビューに勝つ。
- 例外:既にstdlibの組み合わせであるコード(createHash + pipeline)は自前実装ではない。尋ねられたらそう伝え、さらにラップしない。
パス3 — 凝集性と必要性
スロップは蓄積します:編集ごとに追加され、統合されません。以下を探してください:
- 繰り返される条件式 → 一度抽出し、意味で命名する(
check1ではなくnoConfigChanges) - N個のファイルで同じリテラル → その概念を所有するモジュール内の1つの共有定数;利用者はそれをインポートする
- リポジトリヘルパーのローカル再実装 → プライベートコピーを保持する前に、既存のヘルパーをgrepする(
pathExists、出力/フォーマットユーティリティ、エラーヘルパー)。ローカルコピーを削除し、共有のものをインポートする。 - デッドコード:diff内のすべてのヘルパー/型/エクスポートについて、コンシューマを見つける。コンシューマがいなければ、「後で」ではなく今すぐ削除する。
- ヘルパーが担当することを呼び出し元で自前実装している(スペーシング、引用、ID生成) → 動作をヘルパーに移動し、呼び出し元を薄くする
- 同じ事実が2回述べられている(ドキュメントヘッダー + インラインコメント) → 1つを保持する
- 緩く関連する値のタプルを返す関数 → 多くの場合、2つの責任が融合している兆候;分割するか契約を文書化する
次にリポジトリ適合性を確認する:隣接するファイルを読み、その慣用句、命名、エラースタイル、テストパターンに合わせる。ハウススタイルを無視したコードは、正しくても生成されたように読まれる。
パス4 — テスト
- テストは動作を固定しなければならず、バグを固定してはならない。アサーションが「コードがそうしているから」という理由で間違った値をエンコードしている場合、両方を修正する。
- スイートの既存のハーネスとヘルパーを反映させる;同じファイル内に並行するモックスタイルを発明しない。
- すべてのプラットフォーム固有のスキップには1行の理由を含める。
- テスト名は実装ではなくシナリオを説明する。
- 出力形状を変更する場合、削除された形式に対する否定アサーションを追加する。
パス5 — ツールのパリティ、そして検証
- リポジトリで固定されたフォーマッター/リンターのバージョンを使用する(package.jsonを読む — ローカルで異なるメジャーバージョンを実行すると、CIのみで失敗が通る)。
- diffが触れるテストを、スタックされた変更の各レイヤーで実行し、先頭だけでなくすべてのレイヤーで実行する。
- すべての編集後、最終的なdiffをもう一度再監査する:
git diffを実行し、残っているすべてのコメント行をパス1に照らして声に出して読む。密度チェック:git diff | grep -cE '^\+\s*(//|\*)'対 追加行の総数。 - コミットメッセージ:理由を散文で、人間らしく説明する。3行のコミットに箇条書きの変更ログは不要、「強化/改善/堅牢化」も不要。
報告
「これはデスロップ済みですか?」と聞かれた場合:実際のdiffを監査し、証拠とともに答える(密度の数値、何が削除されたか、何が意図的に保持されたか、その理由)。決して記憶から「はい」と答えない。レビュアーがフラグを立てるかもしれない何かを保持した場合(高密度のプロトコルファイルなど)、積極的に名前を挙げ、1文で防御策を述べる。
レビュアーが何かをスロップと呼んだ場合:個々のインスタンスを防御する前にパターンを認める。フラグを立てられなかったファイル(および称賛されたファイル)を含め、カテゴリ全体を修正する。基準を、フラグが立てられた行へのダメージコントロールとしてではなく、規律として適用することが、信頼を再構築する。