ニューロスロップレビュー
ニューロスロップとはスタイルの問題ではなく、コードで裏付けられていない主張や、存在する理由のないコードのことです。 以下の各発見事項は、コードの行、コマンドの出力、消費者不在などの事実によって確認されます。 味覚的な指摘やリンターで捕捉できるものはここには含まれません。
原則。
- 追加するよりも削除するほうが良い。 デッドオプションはドキュメント化するのではなく削除する。マーケティングフレーズは事実に置き換えるか削除する。
- 主張は記憶ではなくコードで確認する。 READMEの行が誤りか正しいかを言う前に、その行が参照しているファイルを開くこと。
- 証拠のない発見は発見ではない。
file:lineやコマンドの出力を示せないなら、それを書いてはいけない。 - 修正は依頼があった場合のみ。デフォルトでは、レビュー結果はリストとして提示する。
1. 作業手順
- 範囲を決める:diff (
git diff main...HEAD)、特定のファイル、または公開前のリポジトリ全体。 - セクション3の検索を実行する — これは候補を出すものであり、発見事項ではない。
- 各候補をコードを読んで確認し、誤検出を除外する。
- セクション2のチェックリストを、検索では捕捉できないものに対して実施する:READMEとコードの比較、UIの状態、抽象化。
- アプリケーションを実行できる場合は、ページを確認する:テンプレート画面やスタイル未適用のコンポーネントはすぐにわかる。
- セクション4の形式でレポートを出力する。
2. チェックリスト
2.1. ジェネレーターとテンプレートの残骸
- ジェネレーターのスタートページやブロック:「Get started by editing」、フレームワークやツールのドキュメントへのリンク、
utm_source=…-project、「Give us a star」、ツールのクラウドサービスに関するブロック。 - テンプレートファイル:
example.spec.ts、テンプレートのタイトルのみをチェックするテスト、public/内のテンプレート由来のSVGや画像、ジェネレーターのREADME。 - 空のプレースホルダー:既にファイルがあるフォルダ内の
.gitkeep、空のindex.ts、コードのないレイヤーフォルダ。
2.2. デッドコードと未完成コード
- 「not implemented」/「pending」をスローする設定値や
switchの分岐。 - 拡張ポイントのように見えるが値が1つしかない列挙型(
type: "graphql" | "msw"において、mswが実装されていない場合など)。 - コメントアウトされたコード、理由や参照のない
TODO、未使用のエクスポート、どこにもインポートされていないpackage.jsonの依存関係。 - 実行できない
package.jsonのスクリプト(削除されたファイルや存在しないターゲットを参照している)。
2.3. ドキュメントとコードの不一致
README、ADR、コメント、コミットメッセージの各主張を確認する:
- 名前とパス: 言及されたファイル、コマンド、環境変数が存在するか(テキストでは
next.config.tsだがリポジトリにはnext.config.jsがある — 発見事項)。 - 動作: 「リクエストは直接送信される」「トークンはJSからアクセスできない」「open redirectから保護されている」 — それを実現するコードを見つけ、境界ケースを確認する。
- バージョンと数値: ライブラリのバージョン、トークンの有効期間、カバレッジ。
- 未来のことなのに現在形で書かれている: 計画されていることが、すでに実装されたかのように記述されている。
- Markdownのリンクとアンカーが正しく機能しているか(アンカーは見出しの変更で壊れることがある)。
2.4. トーン
- 事実ではなく評価語:premium、state-of-the-art、cutting-edge、blazing/lightning-fast、seamless、robust、powerful、modern、production-ready、enterprise-grade、「単なる〜ではない」「プレミアム」「モダン」「パワフル」。
- ドキュメントの見出し、UI、ログ、コミットメッセージでの絵文字。
- 「Designed with ❤️」「Carefully crafted」、意味のないバッジ。
- 言い換えコメント(
getUser()の上に「// ユーザーを取得」)、経緯コメント(「// 修正済み」「// Xに更新」)、すべて大文字のコメント、説明のない「IMPORTANT」マーク。 - 「🚀 Production Roadmap」や「(Premium Aesthetic)」のようなスタイルの見出しやコメント。
置き換えは事実で:何をするのか、なぜそうするのか、どのような制限があるのか。
2.5. 過剰な抽象化
- 実装が1つだけで、テストにフェイクもないインターフェースやファクトリー。
- 消費者がいない、または理由(ADR、READMEのセクション)が記録されていないレイヤー、ゾーン、モノレポパッケージ、状態管理、Storybook。
- 単に名前を変えているだけのラッパー(
useAuthApi = () => useStrictContext(Ctx)— ロジックがなければ許容されるが、ラッパーのラッパーは不可)。 - 「将来のために」:誰も渡さないパラメータ、値が1つしかない設定オプション。
2.6. エラーと非同期処理
- エラーの種類をメッセージテキストで判別している(
message === "unauthorized"、includes("invalid password"))— 理由のコメントやテストがない。 - エラー配列の最初の要素のみをチェックしている(
errors[0])。 throw { status, ... }— Errorオブジェクトではなくオブジェクトをスロー。- 空の
catch、空のデータを返すcatch、.catch(() => {})。 - 共通の実行中Promiseの結果を待機側が確認していない(トークンリフレッシュで「待ってから再試行」が成功したかどうかを確認していない)。
- サーバーの
error.messageをそのままユーザーに表示している。 - ローディング状態、エラー状態、空リスト状態が存在しない。
2.7. セキュリティとプライバシー
- ログ内の個人情報:メール、電話番号、名前、トークン、リクエスト本文。
- リポジトリまたは履歴内のシークレット(
git log -p)、インデックス内の.env、NEXT_PUBLIC_プレフィックス付きのシークレット。 - パラメータによるリダイレクト(
callbackUrl、next、returnTo)にバリデーションがない、またはstartsWith("/")や!startsWith("//")のみのチェック —/\evil.comや制御文字を許容する。 localStorage/sessionStorage内のトークン、読み取り後にアドレスバーから削除されないURL内のトークン。- サニタイザーなしの
dangerouslySetInnerHTML。
2.8. UI
- テーマに存在しないクラス:shadcn/uiからコピーしたトークン(
bg-card、text-muted-foreground、bg-primary)がそれらのトークンを持たないプロジェクトで使用されている — コンポーネントはスタイルなしで表示される。 - トークンが利用可能な状況での、生のHEXや任意の値のコンポーネントでの使用。
onChangeで設定される「入力済み」状態:オートフィルやreset()で壊れる。- 変更可能なリストでの
key={index}、フォーム内のtype属性がないボタン、aria-labelがないアイコンボタン。 - デザインデータから
viewBoxでクリップされたフィルター(シャドウ、グロー)付きのSVG — ロゴの周りに可視の矩形ができる。
2.9. テスト
- 動作ではなくモックをテストしているテスト、大きなツリーのスナップショット。
- 本番または共有バックエンドに対してデータを変更するe2eテスト。
- jsdomがCSSを適用しないために通っているだけのテスト(要素がクラスで「非表示」になっているがテストはそれを見つける)、広いセレクタ(
/password/iが「Show password」にも一致する)。
2.10. Git
- 1つのコミットに無関係な変更が含まれている、「fix stuff」というコミット。
- インデックス内の生成ファイルやビルド成果物(意図的にコミットされた生成APIクライアントを除く)。
- コミットメッセージがdiffの内容と一致していない。
3. 候補の検索
以下のコマンドは候補を出力します。各一致はコードを読んで確認してください。
# ジェネレーターのテンプレートとマーケティング
rg -n -i "utm_source|get started by editing|give us a star|nx cloud|welcome to|carefully crafted|designed with" \
--glob '!**/node_modules/**' --glob '!*.lock' --glob '!pnpm-lock.yaml'
rg -n -i "premium|state-of-the-art|cutting-edge|blazing|lightning|seamless|robust|enterprise-grade|production-ready|プレミアム|モダン|パワフル" \
--glob '!**/node_modules/**'
# コード、UI、ドキュメント内の絵文字
rg -n --pcre2 '[\x{1F300}-\x{1FAFF}\x{2600}-\x{27BF}]' --glob '!**/node_modules/**' --glob '!*.lock'
# 未完成箇所
rg -n -i "not implemented|pending implementation|todo|fixme|hack" --glob '!**/node_modules/**'
# 脆弱なエラー処理
rg -n "errors\?\.\[0\]|errors\[0\]|message ===|message\.includes|\.includes\(['\"][a-z ]+['\"]\)" src app
rg -n "throw \{" src app
rg -n "catch \{\s*\}|catch \(\w*\) \{\s*\}|\.catch\(\(\) => \{\s*\}\)" src app
# 個人情報を含むログ
rg -n "(logger|console)\.\w+\(.*(email|phone|password|token)" src app
# リダイレクトとトークン
rg -n "callbackUrl|returnTo|redirectTo|searchParams.get\(['\"]next" src app
rg -n "localStorage|sessionStorage" src app
# トークンのないshadcnクラス:クラスを見つけて @theme / tailwind-config と照合する
rg -n -o "\b(bg|text|border)-(card|muted|popover|accent|primary|secondary|destructive)(-foreground)?\b" src app | sort -u
# 未使用の依存関係とエクスポート(knipがインストールされている場合)
pnpm dlx knip
# 履歴内のシークレット
git log -p --all | rg -n -i "(api[_-]?key|secret|password|token)\s*[:=]\s*['\"][A-Za-z0-9_\-]{16,}"
git ls-files | rg -n "(^|/)\.env($|\.)" | rg -v "\.env\.example$"
4. レポート形式
発見事項は重要度の降順に並べる:セキュリティとプライバシー → ドキュメントの虚偽の主張 → エラーハンドリングのバグ → デッドコードと未完成コード → テンプレート → トーン。
<file>:<line> — <カテゴリ>
内容: <1文で:何が問題か、事実に基づいて>
重要度: <ユーザー、レビュアー、メンテナーへの影響>
修正: <具体的に。ほとんどの場合「削除」または「事実に置き換え:…」>
末尾に、確認したが発見事項がなかったもの、および確認しなかったもの(例:アプリケーションが起動しなかった)を記載する。発見事項がなければ、その旨を述べ、無理に指摘をしない。