Ревью на нейрослоп
Нейрослоп — это не стиль, а утверждения, которые не подтверждаются, и код, у которого нет причины существовать. Каждая находка ниже проверяется фактом: строкой кода, выводом команды, отсутствием потребителя. Вкусовые замечания и то, что ловит линтер, сюда не входят.
Принципы.
- Удаление лучше дописывания. Мёртвую опцию удаляют, а не документируют; маркетинговую фразу заменяют фактом или убирают.
- Утверждение проверяется по коду, а не по памяти. Прежде чем назвать строку 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, тесты, проверяющие только заголовок шаблона; SVG и картинки из шаблона вpublic/; README генератора. - Пустые заглушки:
.gitkeepв папках, где уже есть файлы; пустыеindex.ts; папки-слои без кода.
2.2. Мёртвое и недоделанное
- Значения конфига или ветки
switch, которые бросают «not implemented» / «pending». - Перечисления с одним значением, которые выглядят как точка расширения
(
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. Лишние абстракции
- Интерфейс или фабрика с одной реализацией и без фейка в тестах.
- Слой, зона, пакет монорепо, стейт-менеджер, Storybook без потребителя или без записанной причины (ADR, раздел README).
- Обёртки, которые только переименовывают (
useAuthApi = () => useStrictContext(Ctx)без логики — допустимо; обёртка над обёрткой — нет). - «На будущее»: параметры, которые никто не передаёт, опции конфига с единственным значением.
2.6. Ошибки и асинхронность
- Вид ошибки определяется по тексту сообщения (
message === "unauthorized",includes("invalid password")) — без комментария-причины и тестов. - Проверяется только первый элемент массива ошибок (
errors[0]). throw { status, ... }— объект вместоError.- Пустой
catch,catch, возвращающий пустые данные,.catch(() => {}). - Общий in-flight промис, результат которого ждущие не читают (refresh токена «ждём, потом повторяем» без проверки, удался ли он).
error.messageсервера показывается пользователю как есть.- Отсутствуют состояния загрузки, ошибки, пустого списка.
2.7. Безопасность и приватность
- Персональные данные в логах: email, телефон, имя; токены; тела запросов.
- Секреты в репозитории или в истории (
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.- SVG из макета с фильтрами (тени, свечение), обрезанными по
viewBox, — видимый прямоугольник вокруг логотипа.
2.9. Тесты
- Тесты, которые проверяют моки, а не поведение; снапшоты больших деревьев.
- e2e против общего или продового бэкенда, меняющие данные.
- Тесты, которые проходят только потому, что jsdom не применяет CSS
(элемент «скрыт» классом, а тест его находит), и широкие селекторы
(
/password/i, который совпадает и с «Show password»).
2.10. Git
- Несвязанные изменения в одном коммите; коммит «fix stuff».
- Сгенерированные файлы и артефакты сборки в индексе (кроме осознанно закоммиченного сгенерированного API-клиента).
- Сообщение коммита описывает не то, что в диффе.
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-конфигом
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> — <категория>
Что: <одно предложение: что не так, с фактом>
Почему важно: <последствие для пользователя, ревьюера или поддержки>
Исправление: <конкретно; чаще всего «удалить» или «заменить на факт: …»>
В конце — что проверено и не дало находок, и что не проверено (например, приложение не запускалось). Если находок нет — так и сказать, без натянутых замечаний.