Skip to content

Viktoriko/005_god_component

Repository files navigation

Таймлайн деградации: CustomerCard v00 → v15

Стенд к статье «пошКОДим: как превратить React-компонент в неуправляемый комбайн — 15 вредных советов».

Один компонент карточки клиента CRM прожил 15 вредных советов: каждый шаг — собираемый и запускаемый снапшот в src/steps/, от 123 строк кода до комбайна с AST-счётом ветвлений 116. По мере деградации появляются воспроизводимые дефекты. Рядом — распиленная версия (src/good/); один общий набор поведенческих тестов подтверждает совпадение поведения в 12 зафиксированных сценариях.

Таймлайн за 5 минут

pnpm install
pnpm dev        # playground: слайдер v00 → v15 → good, вкладки «Диф» и «Счётчик»
pnpm lint       # поддерживаемый код стенда; намеренно плохие снапшоты исключены
pnpm test       # 46 тестов: 12 поведенческих × 2 реализации + smoke и оболочка
pnpm report     # метрики + дифы + графики → report/

В playground:

  • слайдер переключает шаг деградации — на каждом шаге живое приложение (роли, форма, экспорт CSV, фейковый бэкенд с латентностью);
  • «Диф шага» — что именно добавил совет (генерируется сравнением папок-снапшотов, история не завязана на git-коммиты);
  • «Счётчик деградации» — метрики шага и дельта к предыдущему;
  • deep-link сохраняет шаг и вкладку: ?step=v15&tab=metrics открывает финальный счёт напрямую.

Как получить любую цифру статьи

Все значения счётчика и графиков лежат в report/ и воспроизводятся командой pnpm report:

Число из статьи Где лежит Как считается
Строки кода шага report/metrics.jsoncodeLines без пустых строк и комментариев
Хуки, пропсы hooksTotal, propsCount AST TypeScript: вызовы use*, члены интерфейсов *Props
AST-счёт ветвлений компонента maxComplexity авторский сигнал стенда: ветвления + &&/`
Условия в JSX jsxConditions тернарники и логические операторы внутри JSX
Цена монтирования mountCost LOC одинаково оформленного файла mounts/vNN.mount.tsx; число зависит от форматирования и выбранных test helper'ов, эти же файлы гоняет smoke
Дифы шагов report/diffs/vNN.diff git diff --no-index между соседними снапшотами
Графики report/charts/*.svg воспроизводимые исходники из metrics.json; для Habr растрируются в PNG из images/

Структура

src/steps/v00-baseline … v15-final   # 16 снапшотов; совет NN статьи = папка vNN
src/good/                            # обезвреживание: api/ domain/ permissions/ form/ ui/
src/app/                             # playground: слайдер, дифы, счётчик
spec/                                # фейковый бэкенд + общие поведенческие тесты + smoke
mounts/                              # «цена монтирования»: по файлу на снапшот
rig/                                 # metrics · diff · chart · report (Node, без зависимостей)
report/                              # сгенерированные числа — источник всех цифр статьи

Соответствие: совет NN статьи ↔ src/steps/vNN-* ↔ позиция NN слайдера ↔ строка metrics.jsonreport/diffs/vNN.diff.

15 советов коротко

Шаг Совет Акт
v01 «Зачем тебе отдельный API-слой» I. Данные
v02 «Храни как пришло» I
v03 «Заказы? Ещё один fetch рядом» I
v04 «Сам себе кэш» I
v05 «useState на каждое поле» II. Форма
v06 «Производное тоже храни» II
v07 «Валидация здесь же» II
v08 «Ошибки сервера — в тот же объект» II
v09 «Права проверяй в JSX» III. Роли
v10 «Новый вариант — новый boolean-проп» III
v11 «Дели на renderSection()» III
v12 «Свяжи поля магическими строками» III
v13 «Оберни всё в memo» IV. Косметика
v14 «try/catch и console.error» IV
v15 «Наведи порядок комментариями» IV

Чек-лист для code review

Фразы в PR, по которым узнают растущий комбайн (подробности — в статье):

  • «пока положил рядом, потом вынесем» → когда «потом»? (советы 01, 07)
  • «просто ещё один useState» → меняется ли он согласованно с существующими? (05, 06)
  • «добавил флажок, чтобы не плодить компоненты» → какие комбинации флажков осмысленны? (10)
  • «разбил на renderX для читаемости» → какой у секции контракт? (11)
  • «сделал универсальный обработчик» → что случится при переименовании поля? (12)
  • «обернул в memo, стало быстрее» → есть замер? пропсы стабильны? (13)
  • «добавил try/catch, чтобы не падало» → кто узнаёт об ошибке? (14)
  • «причесал файл, добавил секции» → почему секция — не файл? (15)

Машинная страховка: max-lines-per-function и complexity в ESLint как сигнализация с осознанными порогами.

Стек

React 19.2 · TypeScript (strict) · Vite · Vitest + Testing Library. Метрики — свой AST-счётчик на компиляторе TypeScript, без внешних зависимостей. Версии окружения фиксируются в report/env.json.

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages