لدى كل مطور ذلك المجلد. المجلد المسمى utils أو helpers الذي تنسخه من مستودع (repo) إلى آخر. تقوم بلصقه، وتقضي عشرين دقيقة في حذف المراجع لمخططات قواعد البيانات القديمة، واقتلاع فحوصات المصادقة (auth checks) التي لا تنطبق، وإعادة تسمية المتغيرات حتى يتوقف الـ linter الجديد عن الصراخ. كنت أفعل ذلك مع نظام سمات (theming system) قمت ببنائه، وهو Dynamic Theme Kit. بدأ كميزة داخل تطبيق واحد، ولشهور، عاملته كأداة محمولة. كنت مخطئاً. نسخ الكود ليس إعادة استخدام، بل هو تكرار مع خطوات إضافية.
فخ عقلية المشروع الواحد
عندما تبني ميزة داخل مشروع ما، فإنك تضع مئات الافتراضات غير المرئية. قد تفترض لوحة الألوان إعداد CSS-in-JS معين. قد يشير مقياس المسافات (spacing scale) إلى design token من دليل العلامة التجارية لشركتك. قد يستدعي التبديل بين الوضع الفاتح والداكن (light and dark mode) نقطة نهاية (endpoint) لتفضيلات المستخدم فريدة من نوعها لخلفية ذلك التطبيق. تبدو هذه التبعيات غير ضارة لأنها داخل المشروع تكون غير ضارة، فهي تنتمي إلى هناك.
تبدأ المشكلة عندما تحاول استخراج ذلك الكود. تكتشف أن المكون "القابل لإعادة الاستخدام" هو في الواقع شبكة من الروابط الخفية التي تربطه بذاك الكود المصدري الوحيد. تعلمت هذا مع DTK. لقد قام بإنشاء متغيرات السمات، نعم، ولكنه توقع أيضاً هيكل مجلدات محدد. استورد تعريف نوع (type definition) من مكان عميق في دليل الأنواع (types directory) الخاص بالتطبيق الأصلي. وافترض وجود كائن إعدادات (config object) عالمي لا يوجد إلا في ذلك المستودع وحده. لم ألاحظ ذلك أبداً لأنه ضمن ذلك المشروع، كان كل شيء موجوداً دائماً.
تحويل DTK إلى حزمة مستقلة كان بمثابة عملية جراحية، وليس توسعة. لم أكن بحاجة إلى المزيد من الميزات، بل كنت بحاجة إلى روابط أقل.
استخراج Dynamic Theme Kit
كان العمل الأصعب هو الجلوس مع الكود المصدري والسؤال عن كل دالة وكل تصدير (export): هل يخدم هذا منطق السمات (theming logic)، أم يخدم المشروع؟ قمت بتجريد إعدادات التنسيق المسبقة (styling presets). أزلت الافتراض بأن المستهلك سيكون تطبيق React. حذفت لوحات الألوان الافتراضية تماماً. كان المشروع الأصلي يحتوي على جمالية شركات ذات لون كحلي ورمادي (navy-and-slate) مدمجة في الإعدادات الافتراضية، وكان لابد من التخلص منها؛ فلا يمكن للحزمة أن تشحن ألوان علامتك التجارية الخاصة.
ستقوم الحزمة الجديدة بشيء واحد فقط. تأخذ كائن إعدادات (configuration object) — بعض قيم الألوان، وبعض أرقام المسافات، وبعض مقاييس الخطوط — وتقوم بإنشاء خصائص CSS المخصصة (CSS custom properties). هذا كل شيء. هي لا تطبقها، ولا تقرر أين تذهب في الـ DOM الخاص بك، ولا تهتم إذا كنت تستخدم Tailwind أو Styled Components أو HTML بسيط. هي تمنح تطبيقك المتغيرات، ومشروعك يختار كيفية استخدامها.
بدا هذا القيد مقيداً في البداية، لكنه تبين أنه تحرر.
ما الذي يتعطل عندما تحاول فعلياً إعادة استخدامه
قبل أن أنشر أي شيء، كنت بحاجة إلى دليل على أن التجريد (abstraction) صامد بالفعل. سحبت ثلاثة مشاريع شخصية صغيرة من أرشيفي: أداة معاينة markdown، ومتتبع عادات، وصفحة هبوط لحدث ما. لم يشترك أي منها في إطار عمل (framework) أو هيكل مجلدات. قمت بتثبيت DTK محلياً في كل منها وحاولت تطبيق السمات عليها.
فشلت المحاولة الأولى على الفور. كانت أسماء المتغيرات التي أنشأها DTK محددة للغاية. كان يخرج رموزاً (tokens) مثل --primary-action و --background-overlay التي توحي بتخطيط واجهة مستخدم (UI layout) معين. في أداة معاينة markdown، لم تكن تلك الأسماء منطقية؛ فلم يكن هناك زر إجراء (action button)، ولم تكن هناك طبقة تغطية (overlay). قمت بإعادة تسمية منطق الإنشاء لإنتاج أسماء هيكلية محايدة تصف القيمة بدلاً من الأداة (widget).
وجدت أيضاً أن قيمي الافتراضية كانت صارمة للغاية. عندما يمرر المستخدم إعدادات غير مكتملة، كان DTK يملأ الفجوات بقيم تبدو جيدة في لوحة تحكم (dashboard) كثيفة ولكنها تكسر صفحة هبوط بسيطة. انتقلت إلى القيم الافتراضية الشفافة حيث لا يتم عرض الرموز المفقودة ببساطة، مما يسمح للمشروع المستهلك بتحديد قيم الاحتياط (fallbacks) الخاصة به.
ثم كانت هناك التوثيق (documentation). ما بدا بديهياً بالنسبة لي — "فقط مرر كائن إعدادات" — كان غامضاً لشخص يقرأ ملف README في منتصف الليل. أعدت كتابته باستخدام كائنات حقيقية، ومسارات ملفات حقيقية، وشروحات واضحة لما يحدث عند استدعاء الدالة مقابل ما يحتاج تطبيقك إلى فعله بعد ذلك.
عملت هذه المشاريع الشخصية الصغيرة كبيئات اختبار. كانت مخاطرها منخفضة، لكنها كشفت عن عيوب حقيقية لم أكن لأكتشفها بمجرد التحديق في الكود المصدري بمعزل عن غيره.
الاختبار الحقيقي: مرحلة الإنتاج في Web Weavers World
Personal projects are sandboxes. They do not have deadlines, stakeholders, or legacy CSS that predates your package. The real test came when I integrated DTK into Web Weavers World, my business site. This was a live property with existing styles, client expectations, and analytics to consider. If the package broke something, I could not just delete the repo and start over.
I added DTK to the build pipeline, pointed it at a new color configuration, and let it generate a fresh set of CSS variables. The integration took an afternoon, not a week. That was the signal. Previously, adding a new theme meant writing new CSS, hunting down hardcoded hex values in twenty files, and hoping I did not miss an edge case. Now I add a palette to the configuration file, DTK generates the variables, and the rest of the site consumes them. The theme logic went from being a fragile manual process to something I trust enough to hand off to collaborators.
Three Questions That Changed How I Build
Going through this process forced me to formalize a mental checklist I now use before I abstract anything:
- Is this variable truly generic? If the name or the logic references a domain concept from the original project, it stays behind.
- Does this belong in the package or the application? Business rules, brand identities, and layout assumptions live in the app. Plumbing that generates standardized output lives in the package.
- Am I solving a reusable problem or a project-specific one? This is the hardest to answer honestly. We like to think our solutions are universal. Usually they are local.
Answering these questions forced me to simplify my design, often by removing code rather than adding it. DTK taught me that reuse is not a gift you give yourself. It is a discipline you practice by saying no to convenience.
A Different Way to Think About Refactoring
I used to measure refactors by how much shorter they made the code. Fewer lines felt like progress. Now I measure them by how many doors they open. The Dynamic Theme Kit is not elegant because it is concise. It is useful because it survived three unrelated personal projects and a production business site without needing to change its internals.
That is the metric that matters. Code that works once is an expense. Code that works repeatedly is an asset. Before I start any feature now, I stop. I ask if I am building something I will need again. If the answer is yes, I build it differently from the first line. I isolate the inputs. I define the outputs. I remove the assumptions.
The best refactor does not make your code shorter. It makes your code work in places you have not imagined yet.
