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.
どの開発者にも、あのフォルダがあるはずだ。リポジトリからリポジトリへとコピーして使い回す、utils や helpers と名付けられたフォルダだ。それを貼り付け、古いデータベーススキーマへの参照を削除し、適用されない認証チェックを剥ぎ取り、新しいリンターが警告を出し続けないように変数名を書き換える作業に20分を費やす。かつての私は、自作のテーマシステム「Dynamic Theme Kit」でこれを行っていた。最初は一つのアプリケーション内の機能として始まったもので、数ヶ月間、私はそれを持ち運び可能なツールのように扱っていた。だが、それは間違いだった。コードをコピーすることは再利用ではない。それは、余計な手間を加えただけの「重複」なのだ。
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.
単一プロジェクト思考の罠
プロジェクト内で機能を作るとき、そこには何百もの「目に見えない前提条件」が潜んでいる。カラーパレットは特定のCSS-in-JSの設定を前提としているかもしれない。スペーシング・スケールは、会社のブランドガイドにあるデザイントークンを参照しているかもしれない。ライトモードとダークモードの切り替えは、そのアプリのバックエンド特有のユーザー設定エンドポイントを呼び出しているかもしれない。プロジェクト内では、これらの依存関係は無害に感じられる。そこにあるのが当然だからだ。
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.
問題は、そのコードを外に持ち出そうとしたときに始まる。「再利用可能」だと思っていたコンポーネントが、実はその一つのコードベースと結びついた、隠れた文字列の網の目であったことに気づくのだ。私はDTKでこれを学んだ。DTKはテーマ変数を生成してくれた。しかし同時に、特定のフォルダ構造も要求していた。元のアプリの型定義ディレクトリの奥深くから型定義をインポートしていた。そのリポジトリにしか存在しないグローバルな設定オブジェクトの存在を前提としていた。プロジェクト内ではすべてが常に揃っていたため、私はそれに全く気づいていなかった。
Turning DTK into a standalone package meant surgery, not expansion. I did not need more features. I needed fewer connections.
DTKをスタンドアロンのパッケージにするということは、機能の拡張ではなく「外科手術」を意味していた。必要だったのは、より多くの機能ではなく、より少ない接続だった。
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.
Dynamic Theme Kitの抽出
最も困難な作業は、コードベースと向き合い、すべての関数とすべてのエクスポートに対して、「これはテーマのロジックのためのものか、それともプロジェクトのためのものか?」と問いかけることだった。スタイリングのプリセットを削ぎ落とした。利用者がReactアプリケーションであることを前提とするのをやめた。デフォルトのカラーパレットを完全に削除した。元のプロジェクトには、デフォルトとしてネイビーとスレートを基調とした企業的なデザインが組み込まれていた。それは排除しなければならなかった。パッケージにブランドカラーを同梱することはできないからだ。
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.
新しいキットが行うことは、たった一つだけだ。設定オブジェクト(いくつかの色、スペーシングの数値、タイポグラフィのスケールなど)を受け取り、CSSカスタムプロパティを生成すること。それだけだ。プロパティを適用することもしない。DOMのどこに配置するかを決定することもしない。Tailwindを使っていようが、Styled Componentsを使っていようが、あるいはプレーンなHTMLであろうが、知ったことではない。アプリケーションに変数を提供し、その使い方はプロジェクト側に委ねるのだ。
その制約は、最初は制限のように感じられた。しかし、結果としてそれは解放感をもたらした。
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.
実際に再利用しようとしたときに壊れるもの
公開する前に、その抽象化が本当に機能するかどうかの証明が必要だった。アーカイブから3つの小さな個人プロジェクトを取り出した。マークダウン・プレビュー・ツール、習慣トラッカー、そしてイベント用のランディングページだ。どれもフレームワークやフォルダ構造は共通していなかった。それぞれにDTKをローカルにインストールし、テーマを適用しようと試みた。
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.
最初の試みは即座に失敗した。DTKが生成する変数名が具体的すぎたのだ。--primary-action や --background-overlay といった、特定のUIレイアウトを暗示するトークンが出力されていた。マークダウン・プレビューアーにおいて、それらの名前は意味をなさなかった。アクションボタンもなければ、オーバーレイも存在しないからだ。私は生成ロジックを書き換え、ウィジェットではなく「値」を説明するような、中立的で構造的な名前を生成するようにした。
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.
また、デフォルト値が強引すぎることも分かった。ユーザーが不完全な設定を渡した場合、DTKは隙間を埋めるために値を補完したが、それらは密度の高いダッシュボードでは問題なくても、スカスカなランディングページでは表示を崩してしまった。私は「透明なデフォルト値」へと切り替えた。トークンが欠落している場合は単にレンダリングされないようにし、利用するプロジェクト側でフォールバックを定義できるようにした。
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.
それから、ドキュメントだ。「単に設定オブジェクトを渡すだけ」という私にとって当たり前のことは、真夜中にREADMEを読んでいる人にとっては曖昧なものだった。私は、実際のオブジェクト、実際のファイルパス、そして「関数を呼び出したときに何が起こるか」と「その後にアプリケーションが何をすべきか」を明確に説明する内容へと書き直した。
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
真のテスト:Web Weavers Worldでの本番運用
個人プロジェクトはサンドボックスだ。締め切りも、ステークホルダーも、パッケージ以前のレガシーCSSも存在しない。本当の試練は、自分のビジネスサイトである Web Weavers World に DTK を統合した時に訪れた。これは、既存のスタイル、クライアントの期待、そして考慮すべきアナリティクスが存在する、稼働中のプロパティだった。もしパッケージが何かを壊してしまったら、リポジトリを削除して最初からやり直す、といったわけにはいかなかった。
ビルドパイプラインに DTK を追加し、新しいカラー設定を指し示して、新しい CSS 変数のセットを生成させた。統合にかかったのは、1週間ではなく、わずか午後の一時だった。それが決定的なサインだった。以前は、新しいテーマを追加するということは、新しい CSS を書き、20個ものファイルからハードコードされた16進数の値を探し出し、エッジケースを見逃していないことを祈る作業だった。今では、設定ファイルにパレットを追加するだけで、DTK が変数を生成し、サイトの残りの部分がそれを利用する。テーマのロジックは、壊れやすい手作業のプロセスから、共同作業者に任せられるほど信頼できるものへと変わった。
私の構築方法を変えた3つの問い
このプロセスを通じて、何かを抽象化する前に自分自身に課すメンタル・チェックリストを形式化することになった。
- この変数は本当に汎用的か? もし名前やロジックが元のプロジェクトのドメイン概念を参照しているなら、それは(パッケージには含めず)元のプロジェクトに留めておくべきだ。
- これはパッケージに属するものか、それともアプリケーションに属するものか? ビジネスルール、ブランドアイデンティティ、レイアウトの前提条件はアプリケーションに属する。標準化された出力を生成する「配管(plumbing)」はパッケージに属する。
- 解決しようとしているのは、再利用可能な問題か、それともプロジェクト固有の問題か? これに正直に答えるのが最も難しい。私たちは自分の解決策が普遍的であると考えがちだが、たいていは局所的なものだ。
これらの問いに答えることで、コードを追加するのではなく、むしろ削除することによって、デザインを簡素化せざるを得なくなった。DTK は、再利用とは自分に与える贈り物ではなく、利便性を拒絶することによって実践される規律であることを教えてくれた。
リファクタリングに対する異なる考え方
かつて私は、リファクタリングの成果をコードがどれだけ短くなったかで測っていた。行数が減ることは進歩だと感じていた。しかし今では、それがどれだけの「扉」を開いてくれるかで測っている。Dynamic Theme Kit がエレガントなのは、簡潔だからではない。内部構造を変更することなく、3つの無関係な個人プロジェクトと、本番稼働中のビジネスサイトを生き抜いてきたからこそ、有用なのだ。
それこそが重要な指標だ。一度しか機能しないコードは「費用」である。繰り返し機能するコードは「資産」である。今では、新しい機能を作り始める前に、一度立ち止まる。また使うものかどうかを自問する。もし答えが「イエス」なら、最初の一行目から異なる方法で構築する。入力を分離し、出力を定義し、前提条件を取り除くのだ。
最良のリファクタリングとは、コードを短くすることではない。まだ想像もしていない場所で、コードが機能するようにすることなのだ。
