প্রত্যেক ডেভেলপারেরই এমন একটি ফোল্ডার থাকে। যেটিকে utils বা helpers বলা হয় এবং যা আপনি এক রিপোজিটরি থেকে অন্য রিপোজিটরিতে কপি করেন। আপনি এটি পেস্ট করেন, বিশ মিনিট সময় ব্যয় করেন পুরনো ডাটাবেস স্কিমা থেকে রেফারেন্স মুছে ফেলতে, অপ্রাসঙ্গিক অথ চেকগুলো সরিয়ে ফেলতে এবং ভেরিয়েবলগুলোর নাম পরিবর্তন করতে যাতে আপনার নতুন লিন্টার চিৎকার করা বন্ধ করে। আমি আমার তৈরি করা একটি থিমিং সিস্টেম, Dynamic Theme Kit দিয়ে এটি করতাম। এটি একটি অ্যাপ্লিকেশনের ভেতরে একটি ফিচার হিসেবে শুরু হয়েছিল, এবং মাসের পর মাস আমি এটিকে একটি পোর্টেবল টুল হিসেবে ব্যবহার করেছি। আমি ভুল ছিলাম। কোড কপি করা মানে পুনরায় ব্যবহার করা নয়। এটি অতিরিক্ত ধাপসহ ডুপ্লিকেশন মাত্র।
সিঙ্গেল-প্রজেক্ট মাইন্ডসেটের ফাঁদ
যখন আপনি একটি প্রজেক্টের ভেতরে কোনো ফিচার তৈরি করেন, তখন আপনি শত শত অদৃশ্য অনুমান (assumptions) করে ফেলেন। কালার প্যালেট হয়তো একটি নির্দিষ্ট CSS-in-JS সেটআপের কথা ধরে নেয়। স্পেসিং স্কেল হয়তো আপনার কোম্পানির ব্র্যান্ড গাইডের কোনো ডিজাইন টোকেনকে রেফারেন্স হিসেবে ব্যবহার করে। লাইট এবং ডার্ক মোডের মধ্যে টগল করার বিষয়টি হয়তো সেই অ্যাপের ব্যাকএন্ডের জন্য নির্দিষ্ট কোনো ইউজার প্রেফারেন্স এন্ডপয়েন্ট কল করে। এই ডিপেন্ডেন্সিগুলো ক্ষতিকারক মনে হয় না কারণ প্রজেক্টের ভেতরে এগুলো নিরাপদ। এগুলো সেখানেই থাকার কথা।
সমস্যা শুরু হয় যখন আপনি সেই কোডটি আলাদা করার চেষ্টা করেন। আপনি আবিষ্কার করেন যে, "reusable" কম্পোনেন্টটি আসলে সেই একটি কোডবেসের সাথে যুক্ত থাকা কিছু লুকানো স্ট্রিংয়ের জাল। আমি DTK দিয়ে এটি শিখেছি। এটি থিম ভেরিয়েবল তৈরি করত, ঠিক আছে। কিন্তু এটি একটি নির্দিষ্ট ফোল্ডার স্ট্রাকচারও আশা করত। এটি মূল অ্যাপের টাইপস ডিরেক্টরির গভীরে থাকা কোনো টাইপ ডেফিনিশন ইমপোর্ট করত। এটি একটি গ্লোবাল কনফিগ অবজেক্টের উপস্থিতির কথা ধরে নিয়ে চলত যা শুধুমাত্র সেই একটি রিপোজিটরিতেই ছিল। আমি কখনোই এটি লক্ষ্য করিনি কারণ সেই প্রজেক্টের মধ্যে সবকিছু সবসময় উপস্থিত ছিল।
DTK-কে একটি স্ট্যান্ডঅ্যালোন প্যাকেজে রূপান্তর করা মানে ছিল সার্জারি করা, সম্প্রসারণ নয়। আমার আরও ফিচারের প্রয়োজন ছিল না। আমার প্রয়োজন ছিল সংযোগ কমিয়ে আনা।
Dynamic Theme Kit-কে আলাদা করা
সবচেয়ে কঠিন কাজ ছিল কোডবেসের সামনে বসে প্রতিটি ফাংশন এবং প্রতিটি এক্সপোর্টের জন্য নিজেকে প্রশ্ন করা: এটি কি থিমিং লজিকের কাজ করছে, নাকি এটি প্রজেক্টের কাজ করছে? আমি স্টাইলিং প্রিসেটগুলো সরিয়ে ফেলেছি। আমি এই ধারণাটি সরিয়ে দিয়েছি যে ব্যবহারকারী একটি React অ্যাপ্লিকেশন হবে। আমি ডিফল্ট কালার প্যালেটগুলো পুরোপুরি মুছে ফেলেছি। মূল প্রজেক্টের ডিফল্টগুলোতে একটি নেভি-অ্যান্ড-স্লেট কর্পোরেট নান্দনিকতা (aesthetic) ছিল। সেটি বাদ দিতে হয়েছিল। একটি প্যাকেজ আপনার ব্র্যান্ড কালার শিপ করতে পারে না।
নতুন কিটটি ঠিক একটি কাজ করবে। এটি একটি কনফিগারেশন অবজেক্ট গ্রহণ করবে—কিছু কালার ভ্যালু, কিছু স্পেসিং নম্বর, কিছু টাইপোগ্রাফি স্কেল—এবং এটি CSS custom properties তৈরি করবে। ব্যস, এটুকুই। এটি সেগুলো অ্যাপ্লাই করে না। এটি আপনার DOM-এ সেগুলো কোথায় যাবে তা ঠিক করে না। আপনি Tailwind, Styled Components বা সাধারণ HTML ব্যবহার করছেন কি না, তাতে এর কিছু যায় আসে না। এটি আপনার অ্যাপ্লিকেশনকে ভেরিয়েবলগুলো দেয়, আর আপনার প্রজেক্ট ঠিক করে সেগুলো কীভাবে ব্যবহার করতে হবে।
সেই সীমাবদ্ধতাটি প্রথমে সীমিত মনে হয়েছিল। কিন্তু পরে এটি মুক্তিদায়ক বলে প্রমাণিত হলো।
যখন আপনি আসলে এটি পুনরায় ব্যবহার করার চেষ্টা করেন তখন কী কী ভেঙে যায়
কিছু পাবলিশ করার আগে, আমার প্রমাণ দরকার ছিল যে এই অ্যাবস্ট্রাকশনটি আসলেই কার্যকর কি না। আমি আমার আর্কাইভ থেকে তিনটি ছোট ব্যক্তিগত প্রজেক্ট বের করলাম: একটি মার্কডাউন প্রিভিউ টুল, একটি হ্যাবিট ট্র্যাকার এবং একটি ইভেন্টের জন্য ল্যান্ডিং পেজ। সেগুলোর কোনোটিরই ফ্রেমওয়ার্ক বা ফোল্ডার স্ট্রাকচার এক ছিল না। আমি প্রতিটি প্রজেক্টে লোকালি DTK ইনস্টল করলাম এবং সেগুলোকে থিম করার চেষ্টা করলাম।
প্রথম প্রচেষ্টাটি সাথে সাথে ব্যর্থ হলো। DTK যে ভেরিয়েবল নামগুলো তৈরি করছিল সেগুলো ছিল অত্যন্ত সুনির্দিষ্ট। এটি --primary-action এবং --background-overlay-এর মতো টোকেন আউটপুট দিচ্ছিল যা একটি নির্দিষ্ট UI লেআউটের ইঙ্গিত দেয়। মার্কডাউন প্রিভিউয়ারের ক্ষেত্রে সেই নামগুলোর কোনো মানে ছিল না। সেখানে কোনো অ্যাকশন বাটন ছিল না। কোনো ওভারলে ছিল না। আমি জেনারেশন লজিকটি এমনভাবে পরিবর্তন করলাম যাতে এটি উইজেটের পরিবর্তে ভ্যালু বা মান বর্ণনা করে এমন নিরপেক্ষ এবং স্ট্রাকচারাল নাম তৈরি করে।
আমি আরও দেখলাম যে আমার ডিফল্ট ভ্যালুগুলো খুব বেশি আক্রমণাত্মক ছিল। যখন একজন ব্যবহারকারী একটি অসম্পূর্ণ কনফিগ পাস করত, DTK এমন সব ভ্যালু দিয়ে গ্যাপগুলো পূরণ করত যা একটি ঘন ড্যাশবোর্ডে ঠিক দেখাত কিন্তু একটি ফাঁকা ল্যান্ডিং পেজে ভেঙে পড়ত। আমি ট্রান্সপারেন্ট ডিফল্ট পদ্ধতিতে চলে গেলাম যেখানে মিসিং টোকেনগুলো কেবল রেন্ডার হবে না, ফলে ব্যবহারকারী প্রজেক্ট নিজেই তার নিজস্ব ফলব্যাক (fallback) নির্ধারণ করতে পারবে।
তারপর ছিল ডকুমেন্টেশন। যা আমার কাছে স্পষ্ট ছিল—"শুধু একটি কনফিগ অবজেক্ট পাস করুন"—মধ্যরাতে 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.
