В апреле я съездил на HolyJS. Еще до поездки в расписании конференции моё внимание привлек доклад Виктора Хомякова «Удаление мертвого кода в проекте: практическое руководство». Послушав его, я понял, что могу использовать полученные знания в своем текущем проекте, при этом не затрачивая много усилий. В этой статье я расскажу, что у меня получилось.
Что такое мертвый код
Мертвый код — это участки кода или зависимостей, которые:
Никогда не выполняются
Не используются нигде в проекте
Включают ненужные пакеты и дубли транзитивных зависимостей (например, пакеты в dependencies)
Невозможно выполнить
import someLib from 'someLib';
if (Math.random() > 1) {
someLib.doSomething();
}
Какой план
Мы решили заняться оптимизацией производительности нашего приложения в три этапа:
Анализ производительности — оценка текущего веса и скорости приложения
Удаление мертвого кода
Автоматизация — внедрение автоматизированных процессов для оптимизации
Анализ производительности
Наше приложение собирается при помощи webpack. Для анализа бандлов использовал webpack-bundle-analyzer. Результаты получились следующие:

Время сборки: 10–16 минут.
Проблема: серверный бандл содержит шрифтовые ассеты.
Удаление мертвого кода
Бандл проанализировали, печальные выводы сделали. Переходим к практике!
Дедуплиĸация npm
Пакетный менеджер у нас npm. Поэтому вот что я делал:
Диагностировал через команды
npm list --include=prod
npm find-dupes
Устранил дубли
npm dedupe
added 28 packages , removed 57 packages , ¨NBSP; and changed 117 packages in 20s
Сказал команде поправить конфиг npm
npm config set prefer-dedupe true

Настройки ESLint и tsconfig
В .eslintrs добавлены правила:
"no-unused-vars": "error",
"no-unused-private-class-members": "error",
"no-unreachable": "error",
"no-unused-expressions": "error"
А в tsconfig.json добавлено:
"noUnusedLocals": true /* Enable error reporting when a local variables aren't read. */,
"noUnusedParameters": true /* Raise an error when a function parameter isn't read */,
Настройка knip и удаление мертвечины
Для этого я выбрал инструмент knip. Несмотря на сложность настройки, он покрывает большинство моих потребностей в плане статистического анализа проекта, да и разработчики ESLint его рекомендуют.
Помимо knip рассматривал ts-prune и TSR, но первый больше не развивается, а у второго мне не хватило более тонкой настройки конфигурации.
Результаты проверки knip

Выглядит НЕ круто!
Фиксим
Сначала разобрался с зависимостями, затем пришлось править конфиг knip, добавлять новые точки входа, игноры. Итоговый knip.json вышел таким:
{
"$schema": "https://unpkg.com/knip@5/schema.json",
"entry": ["src/client/index.tsx", "src/server/server.js", "bin/dev.js", "jest.config.js", "jest.setup.js"],
"project": ["src/**/*.{ts,tsx,js,jsx,mjs}"],
"ignore": ["**/*.global.css", "**/*.td.ts", "**/index.ts"],
"webpack": {
"config": ["webpack.config.js", "cfg/webpack.client.config.js", "cfg/webpack.server.config.js"]
},
"babel": {
"config": ["babel.config.js"]
},
"ignoreDependencies": [
"enzyme",
"enzyme-to-json",
"@types/enzyme",
"@types/react-router-dom",
"prettier",
"prettier-eslint"
]
}
Потом разрешил удалять неиспользуемые файлы. Реализуется через scripts в package.json:
"knip": "knip",
"knip:md": "knip --reporter markdown > knip-report.md",
"knip:fix": "knip --fix-type exports,types,files --allow-remove-files"
knip:md
формирует отчет в .md формате.knip:fix
позволяет knip фиксить обнаруженные проблемы. Флаг --allow-remove-files
дает удалять неиспользуемые файлы.
В результате проделанных действий все проблемы, что выявил knip, были решены.
Что дальше:
Запихнуть проверку knip в pre commit hooks.
В knip.json добавлены '**/index.ts' в игнор. Нужно правильно обрабатывать такие файлы.
Итоги
Папка |
До оптимизации |
После оптимизации |
Разница (МБ) |
Разница (%) |
---|---|---|---|---|
node_modules |
825M |
669M |
-156M |
▼ 18.90% |
dist |
12.8M |
8.0M |
-4.8M |
▼ 37.50% |
client |
7.1M |
5.9M |
-1.2M |
▼ 16.90% |
server |
5.7M |
2.1M |
-3.6M |
▼ 63.16% |
Раньше приложение на поде поднималось 10-16 минут, теперь за 3–7 минут.
Комментарии (8)
nihil-pro
27.05.2025 06:03Однажды, начав работу в новой команде над проектом, которому было меньше года, я пошёл смотреть… нет, не размер бандла —
package.json
. Я вообще люблю начинать с него. И сразу начал выкидывать лишнее.Без долгих предисловий:
package.json
умер.Сначала я выбросил
axios
, заменив его наfetch
. Потомmoment.js
иdate-fns
(да, обе библиотеки использовались) — заменил наIntl.DateTimeFormat
. Библиотекуuuid
— на встроенныйcrypto.randomUUID
, и так далее. Через пару месяцев бандл «похудел» с 2 МБ до 700 КБ.И жили они долго и счастливо в мире и гармонии…
В общем, я к чему: иногда стоит заглянуть в эту чёрную дыру под названием
node_modules
. Потому что затащить еще пару зависимостей, которые будут искать лишние зависимости, вы всегда успеете.
Katrain
А как проверяли, что изменения не привели к багам? Просто е2е + юнит тесты или что-то подобное?
happydeadman_95 Автор
Верно, в основном это было e2e тестирование