Every developer has that folder. The one called utils or helpers that you copy from repo to repo. You paste it in, spend twenty minutes deleting references to old database schemas, ripping out auth checks that don’t apply, and renaming variables so your new linter stops screaming. I used to do this with a theming system I’d built, the Dynamic Theme Kit. It started as a feature inside one application, and for months, I treated it like a portable tool. I was wrong. Copying code is not reuse. It is duplication with extra steps.

The Trap of the Single-Project Mindset

When you build a feature inside a project, you make hundreds of invisible assumptions. The color palette might assume a certain CSS-in-JS setup. The spacing scale might reference a design token from your company’s brand guide. The toggle between light and dark mode might call a user preference endpoint unique to that app’s backend. These dependencies feel harmless because inside the project, they are harmless. They belong there.

The problem starts when you try to lift that code out. You discover that the “reusable” component is actually a web of hidden strings connecting it to that one codebase. I learned this with DTK. It generated theme variables, yes. But it also expected a specific folder structure. It imported a type definition from somewhere deep in the original app’s types directory. It assumed the presence of a global config object that only existed in that one repository. I had never noticed because within that project, everything was always present.

Turning DTK into a standalone package meant surgery, not expansion. I did not need more features. I needed fewer connections.

Extracting the Dynamic Theme Kit

The hardest work was sitting down with the codebase and asking, for every function and every export: does this serve the theming logic, or does it serve the project? I stripped out styling presets. I removed the assumption that the consumer would be a React application. I deleted the default color palettes entirely. The original project had a navy-and-slate corporate aesthetic baked into the defaults. That had to go. A package cannot ship your brand colors.

The new kit would do exactly one thing. It takes a configuration object—some color values, some spacing numbers, some typography scales—and it generates CSS custom properties. That is it. It does not apply them. It does not decide where they go in your DOM. It does not care if you use Tailwind, Styled Components, or plain HTML. It gives your application the variables, and your project chooses how to use them.

That constraint felt limiting at first. It turned out to be liberating.

What Breaks When You Actually Try to Reuse It

Before I published anything, I needed proof that the abstraction actually held water. I pulled three small personal projects out of my archive: a markdown preview tool, a habit tracker, and a landing page for an event. None of them shared a framework or a folder structure. I installed DTK locally in each one and tried to theme them.

The first attempt failed immediately. The variable names DTK generated were too specific. It was outputting tokens like --primary-action and --background-overlay that implied a certain UI layout. In the markdown previewer, those names made no sense. There was no action button. There was no overlay. I renamed the generation logic to produce neutral, structural names that described the value rather than the widget.

I also found that my default values were too aggressive. When a user passed an incomplete config, DTK would fill in gaps with values that looked fine in a dense dashboard but broke on a sparse landing page. I switched to transparent defaults where missing tokens simply did not render, letting the consuming project define its own fallbacks.

Then there was the documentation. What seemed obvious to me—"just pass a config object"—was vague to someone reading the README at midnight. I rewrote it with real objects, real file paths, and clear explanations of what happens when you call the function versus what your application needs to do after.

These small personal projects acted as test beds. They were low stakes, but they exposed real flaws I would not have caught by staring at the source code in isolation.

The Real Test: Production at Web Weavers World

Личные проекты — это песочницы. У них нет дедлайнов, стейкхолдеров или устаревшего CSS, который появился раньше вашего пакета. Настоящее испытание началось, когда я интегрировал DTK в Web Weavers World, свой бизнес-сайт. Это был работающий ресурс с уже существующими стилями, ожиданиями клиента и необходимостью учитывать аналитику. Если бы пакет что-то сломал, я не мог бы просто удалить репозиторий и начать всё сначала.

Я добавил DTK в конвейер сборки, указал новую конфигурацию цветов и позволил ему сгенерировать свежий набор CSS-переменных. Интеграция заняла один вечер, а не неделю. Это стало сигналом. Раньше добавление новой темы означало написание нового CSS, поиск захардкоженных HEX-значений в двадцати файлах и надежду на то, что я не упустил какой-нибудь пограничный случай. Теперь я добавляю палитру в конфигурационный файл, DTK генерирует переменные, а остальная часть сайта их использует. Логика тем превратилась из хрупкого ручного процесса во что-то, чему я достаточно доверяю, чтобы передать это коллегам.

Три вопроса, которые изменили мой подход к разработке

Прохождение через этот процесс заставило меня формализовать ментальный чек-лист, который я теперь использую перед тем, как что-либо абстрагировать:

  • Действительно ли эта переменная универсальна? Если имя или логика ссылаются на концепцию предметной области оригинального проекта, она должна остаться в нем.
  • Это относится к пакету или к приложению? Бизнес-правила, айдентика бренда и допущения по верстке живут в приложении. Механизмы, генерирующие стандартизированный результат, живут в пакете.
  • Я решаю универсальную задачу или специфичную для конкретного проекта? На этот вопрос труднее всего ответить честно. Нам нравится думать, что наши решения универсальны. Обычно они локальны.

Ответы на эти вопросы заставили меня упрощать дизайн, часто путем удаления кода, а не его добавления. DTK научил меня тому, что повторное использование — это не подарок самому себе. Это дисциплина, которую вы практикуете, говоря «нет» удобству.

Другой взгляд на рефакторинг

Раньше я оценивал рефакторинг по тому, насколько короче становился код. Меньшее количество строк казалось прогрессом. Теперь я оцениваю его по тому, сколько дверей он открывает. Dynamic Theme Kit элегантен не потому, что он лаконичен. Он полезен потому, что он пережил три несвязанных личных проекта и рабочий бизнес-сайт, не потребовав изменений во внутренней структуре.

Это и есть важная метрика. Код, который работает один раз, — это расход. Код, который работает многократно, — это актив. Теперь, прежде чем приступить к любой новой функции, я останавливаюсь. Я спрашиваю себя, не понадобится ли мне это снова. Если ответ «да», я строю это иначе с самой первой строки. Я изолирую входные данные. Я определяю выходные данные. Я убираю допущения.

Лучший рефакторинг не делает ваш код короче. Он делает так, чтобы ваш код работал в местах, о которых вы еще даже не задумывались.