Setiap pembangun mempunyai folder itu. Folder yang dinamakan utils atau helpers yang anda salin dari satu repo ke repo yang lain. Anda menampalnya masuk, menghabiskan masa dua puluh minit memadam rujukan ke skema pangkalan data lama, membuang semakan auth yang tidak berkaitan, dan menamakan semula pemboleh ubah supaya linter baharu anda berhenti mengeluarkan amaran. Saya pernah melakukan ini dengan sistem penentuan tema yang saya bina, Dynamic Theme Kit. Ia bermula sebagai satu ciri di dalam satu aplikasi, dan selama berbulan-bulan, saya melayannya seperti alat mudah alih. Saya silap. Menyalin kod bukanlah penggunaan semula. Ia adalah penduplikatan dengan langkah tambahan.

Perangkap Minda Projek Tunggal

Apabila anda membina satu ciri di dalam sesebuah projek, anda membuat beratus-ratus andaian yang tidak kelihatan. Palet warna mungkin mengandaikan tetapan CSS-in-JS tertentu. Skala jarak mungkin merujuk kepada token reka bentuk daripada panduan jenama syarikat anda. Pertukaran antara mod terang dan gelap mungkin memanggil endpoint pilihan pengguna yang unik untuk backend aplikasi tersebut. Kebergantungan ini terasa tidak berbahaya kerana di dalam projek tersebut, ia memang tidak berbahaya. Ia memang milik projek itu.

Masalah bermula apabila anda cuba mengeluarkan kod tersebut. Anda mendapati bahawa komponen yang "boleh digunakan semula" itu sebenarnya adalah rangkaian rentetan tersembunyi yang menghubungkannya kepada satu kod sumber tersebut. Saya mempelajari perkara ini dengan DTK. Ia menjana pemboleh ubah tema, ya. Tetapi ia juga menjangkakan struktur folder yang khusus. Ia mengimport definisi jenis (type definition) dari suatu tempat yang jauh di dalam direktori jenis aplikasi asal. Ia mengandaikan kehadiran objek konfigurasi global yang hanya wujud dalam satu repositori itu sahaja. Saya tidak pernah menyedarinya kerana dalam projek tersebut, semuanya sentiasa ada.

Menukarkan DTK menjadi pakej berdiri sendiri bermaksud melakukan pembedahan, bukan pengembangan. Saya tidak memerlukan lebih banyak ciri. Saya memerlukan lebih sedikit sambungan.

Mengekstrak Dynamic Theme Kit

Kerja yang paling sukar adalah duduk bersama kod sumber dan bertanya, bagi setiap fungsi dan setiap eksport: adakah ini berfungsi untuk logik penentuan tema, atau adakah ini berfungsi untuk projek? Saya membuang tetapan gaya (styling presets). Saya membuang andaian bahawa pengguna akan menggunakan aplikasi React. Saya memadamkan palet warna lalai sepenuhnya. Projek asal mempunyai estetik korporat navy-and-slate yang terbina dalam tetapan lalai. Itu perlu dibuang. Sesebuah pakej tidak boleh menyertakan warna jenama anda.

Kit baharu ini hanya akan melakukan satu perkara sahaja. Ia mengambil satu objek konfigurasi—beberapa nilai warna, beberapa nombor jarak, beberapa skala tipografi—dan ia menjana CSS custom properties. Itu sahaja. Ia tidak menerapkannya. Ia tidak menentukan di mana ia harus diletakkan dalam DOM anda. Ia tidak peduli jika anda menggunakan Tailwind, Styled Components, atau HTML biasa. Ia memberikan pemboleh ubah kepada aplikasi anda, dan projek anda memilih cara untuk menggunakannya.

Kekangan itu terasa mengehadkan pada mulanya. Namun, ia rupa-rupanya membebaskan.

Apa yang Rosak Apabila Anda Benar-benar Cuba Menggunakannya Semula

Sebelum saya menerbitkan apa-apa, saya memerlukan bukti bahawa abstraksi tersebut benar-benar kukuh. Saya mengambil tiga projek peribadi kecil daripada arkib saya: satu alat pratonton markdown, satu penjejak tabiat, dan satu laman pendaratan (landing page) untuk sesuatu acara. Tiada satu pun daripadanya berkongsi rangka kerja atau struktur folder yang sama. Saya memasang DTK secara tempatan dalam setiap satu dan cuba menetapkan temanya.

Percubaan pertama gagal serta-merta. Nama pemboleh ubah yang dijana oleh DTK terlalu spesifik. Ia mengeluarkan token seperti --primary-action dan --background-overlay yang membayangkan susun atur UI tertentu. Dalam alat pratonton markdown, nama-nama tersebut tidak masuk akal. Tiada butang tindakan. Tiada overlay. Saya menamakan semula logik penjanaan untuk menghasilkan nama struktur yang neutral yang menerangkan nilai dan bukannya widget.

Saya juga mendapati bahawa nilai lalai saya terlalu agresif. Apabila pengguna memberikan konfigurasi yang tidak lengkap, DTK akan mengisi kekosongan dengan nilai yang kelihatan baik dalam papan pemuka (dashboard) yang padat tetapi merosakkan laman pendaratan yang ringkas. Saya beralih kepada nilai lalai telus (transparent defaults) di mana token yang hilang tidak akan dipaparkan, membolehkan projek pengguna menentukan nilai sandaran (fallbacks) mereka sendiri.

Kemudian ada pula isu dokumentasi. Apa yang nampak jelas bagi saya—"hanya masukkan objek konfigurasi"—adalah samar bagi seseorang yang membaca README pada tengah malam. Saya menulis semula dokumentasi tersebut dengan objek sebenar, laluan fail sebenar, dan penjelasan jelas tentang apa yang berlaku apabila anda memanggil fungsi tersebut berbanding apa yang perlu dilakukan oleh aplikasi anda selepas itu.

Projek peribadi kecil ini bertindak sebagai medan ujian. Risikonya rendah, tetapi ia mendedahkan kecacatan sebenar yang tidak akan saya kesan jika hanya merenung kod sumber secara berasingan.

Ujian Sebenar: Pengeluaran di Web Weavers World

Projek peribadi adalah sandbox. Ia tidak mempunyai tarikh akhir, pemegang taruh, atau CSS legasi yang mendahului pakej anda. Ujian sebenar bermula apabila saya menyepadukan DTK ke dalam Web Weavers World, laman perniagaan saya. Ini adalah aset langsung dengan gaya sedia ada, jangkaan pelanggan, dan analitik yang perlu dipertimbangkan. Jika pakej tersebut merosakkan sesuatu, saya tidak boleh sekadar memadam repositori dan bermula semula.

Saya menambah DTK ke dalam saluran binaan (build pipeline), menghalakannya ke konfigurasi warna baharu, dan membiarkannya menjana set pemboleh ubah CSS yang baharu. Integrasi tersebut hanya mengambil masa satu petang, bukan seminggu. Itulah petandanya. Sebelum ini, menambah tema baharu bermaksud menulis CSS baharu, mencari nilai hex yang hardcoded dalam dua puluh fail, dan berharap saya tidak terlepas sebarang edge case. Sekarang, saya hanya menambah palet ke dalam fail konfigurasi, DTK menjana pemboleh ubah tersebut, dan selebihnya laman web akan menggunakannya. Logik tema telah berubah daripada proses manual yang rapuh kepada sesuatu yang cukup saya percayai untuk diserahkan kepada kolaborator.

Tiga Soalan Yang Mengubah Cara Saya Membina

Melalui proses ini memaksa saya untuk merasmikan senarai semak mental yang kini saya gunakan sebelum saya melakukan pengabstrakan terhadap apa-apa sahaja:

  • Adakah pemboleh ubah ini benar-benar generik? Jika nama atau logiknya merujuk kepada konsep domain daripada projek asal, ia harus ditinggalkan.
  • Adakah ini milik pakej atau aplikasi? Peraturan perniagaan, identiti jenama, dan andaian susun atur berada dalam aplikasi. Sistem sokongan (plumbing) yang menjana output piawai berada dalam pakej.
  • Adakah saya sedang menyelesaikan masalah yang boleh digunakan semula atau masalah khusus projek? Ini adalah yang paling sukar untuk dijawab dengan jujur. Kita suka berfikir bahawa penyelesaian kita adalah universal. Kebiasaannya, ia adalah bersifat lokal.

Menjawab soalan-soalan ini memaksa saya untuk memudahkan reka bentuk saya, selalunya dengan membuang kod dan bukannya menambahnya. DTK mengajar saya bahawa penggunaan semula bukanlah hadiah yang anda berikan kepada diri sendiri. Ia adalah satu disiplin yang anda amalkan dengan berkata "tidak" kepada kemudahan.

Cara Berbeza Untuk Berfikir Tentang Refactoring

Dahulu saya mengukur refactor berdasarkan sejauh mana ia memendekkan kod. Baris yang lebih sedikit terasa seperti kemajuan. Sekarang saya mengukurnya berdasarkan berapa banyak pintu yang dibukanya. Dynamic Theme Kit tidak elegan kerana ia ringkas. Ia berguna kerana ia berjaya bertahan melalui tiga projek peribadi yang tidak berkaitan dan satu laman perniagaan produksi tanpa perlu mengubah bahagian dalamannya.

Itulah metrik yang penting. Kod yang berfungsi sekali adalah satu perbelanjaan. Kod yang berfungsi berulang kali adalah satu aset. Sebelum saya memulakan sebarang ciri sekarang, saya berhenti seketika. Saya bertanya sama ada saya sedang membina sesuatu yang akan saya perlukan lagi. Jika jawapannya ya, saya membinanya secara berbeza sejak baris pertama lagi. Saya mengasingkan input. Saya menetapkan output. Saya membuang andaian.

Refactor terbaik tidak menjadikan kod anda lebih pendek. Ia menjadikan kod anda berfungsi di tempat yang belum anda bayangkan lagi.