জন ওস্টারহুটের ব্যবহারিক সফটওয়্যার ডিজাইন, Tcl-এর ঐতিহ্য, Ousterhout বনাম Brooks বিতর্ক এবং কীভাবে জটিলতা পণ্যকে ডুবিয়ে দেয়—এসব ধারণা অন্বেষণ করুন।

জন ওস্টারহুট একজন কম্পিউটার বিজ্ঞানী ও ইঞ্জিনিয়ার যিনি গবেষণা ও বাস্তব সিস্টেম—দুইই দিকেই কাজ করেছেন। তিনি Tcl প্রোগ্রামিং ভাষা তৈরি করেছেন, আধুনিক ফাইল সিস্টেমগুলোর গঠনগুলোতে অবদান রেখেছেন, এবং পরবর্তীতে বহু বছরের অভিজ্ঞতা থেকে একটি সরল, কিছুটা অস্বস্তিকর দাবি তৈরি করেছিলেন: জটিলতা সফটওয়্যারের প্রধান শত্রু।
এই বার্তাটি এখনো সমসাময়িক কারণ বেশিরভাগ দল ফিচারের বা শ্রমের অভাবে বিফল হয় না—তারা বিফল হয় কারণ তাদের সিস্টেম (এবং সংগঠন) বোঝা কঠিন, বদলানো কঠিন, এবং ভেঙে পড়া সহজ হয়ে পড়ে। জটিলতা কেবল ইঞ্জিনিয়ারদের ধীর করে না; তা প্রোডাক্ট সিদ্ধান্ত, রোডম্যাপের আত্মবিশ্বাস, গ্রাহকের আস্থা, ইনসিডেন্ট ফ্রিকোয়েন্সি এবং এমনকি হায়ারিং-এও প্রবাহিত হয়—কারণ অনবোর্ডিং মাসজুড়ে কঠিন হয়ে ওঠে।
ওস্টারহুটের ফ্রেমিংটি ব্যবহারিক: যখন কোনো সিস্টেম বিশেষ কেস, ব্যতিক্রম, লুকানো নির্ভরশীলতা, এবং "একবারের জন্য" সমাধান জমা করে, খরচ কেবল কোডবেসে সীমিত থাকে না। পুরো প্রোডাক্টটি বিকাশের জন্য আরো ব্যয়বহুল হয়ে ওঠে। ফিচার যোগ করতে বেশি সময় লাগে, QA কঠিন হয়, রিলিজ ঝুঁকিপূর্ণ হয়, এবং টিমগুলো উন্নতি এড়াতে শুরু করে কারণ যেকোনো জিনিসে ছোঁয়া বিপজ্জনক মনে হয়।
এটি একাডেমিক বিশুদ্ধতার জন্য আহ্বান নয়। এটি মনে রাখার একটি উপদেশ যে প্রতিটি শর্টকাটের ওপর সুদের অর্থ দিতে হয়—আর জটিলতা সবচেয়ে বেশি সুদের ঋণ।
খবরটি কংক্রিট (কেবল মোটিভেশনাল নয়) করতে আমরা ওস্টারহুটের বার্তাকে তিন দিক থেকে দেখবো:
এটি শুধুই ভাষাপ্রেমীদের জন্য লেখা নয়। যদি আপনি পণ্য নির্মাণ করেন, টিম নেতৃত্ব দেন, বা রোডম্যাপ ট্রেডঅফ করেন, তাহলে আপনি চিহ্নিত করতে পারবেন কিভাবে জটিলতা আগে দেখা যায়, কিভাবে এটিকে ইনস্টিটিউশনালাইজড হওয়া থেকে রোধ করা যায়, এবং কিভাবে সরলতাকে প্রথম-শ্রেণীর বিবেচ্য বিষয়ে পরিণত করা যায়—নতুন রিলিজের পরে একটি ‘সাইট-নোট’ নয়।
জটিলতা মানে নেই “অনেক কোড” বা “কঠিন ম্যাথ”। এটি সেই ফাঁক যা আপনি ভাবেন সিস্টেমটি কী করবে যখন আপনি পরিবর্তন করবেন এবং এটি ইতিবাচকভাবে কী করে—এর মধ্যে পড়ে। একটি সিস্টেম জটিল যখন ছোট সম্পাদনাগুলো ঝুঁকিপূর্ণ মনে হয়—কারণ আপনি বিস্তারের ভবিষ্যদ্বাণী করতে পারবেন না।
স্বাস্থ্যবান কোডে আপনি উত্তর দিতে পারেন: “যদি আমরা এটা পরিবর্তন করি, আর কী ভাঙতে পারে?” জটিলতা সেটি ব্যয়বহুল করে।
এটি প্রায়শই লুকিয়ে থাকে:
দলগুলো জটিলতাকে অনুভব করে—শিপিং ধীর হওয়া (অনুসন্ধানে বেশি সময়), আরও বাগ (কারণ আচরণ আশ্চর্যজনক), এবং ভঙ্গুর সিস্টেম (পরিবর্তন করতে বহু মানুষ ও সার্ভিসকে সমন্বয় করা লাগে)। এটা অনবোর্ডিং-ও করেও ভ্যাপসিত করে: নতুন সহকর্মীরা মানসিক মডেল তৈরি করতে পারে না, তাই তারা কোর ফ্লো-এ হাত দিতে ভয় পায়।
কিছু জটিলতা অপরিহার্য: ব্যবসায়িক নিয়ম, কমপ্লায়েন্স চাহিদা, বাস্তব বিশ্বের এজ কেস। আপনি এগুলো মুছতে পারবেন না।
কিন্তু অনেকটাই আকস্মিক: বিভ্রান্তিকর API, নকল করা লজিক, “অস্থায়ী” ফ্ল্যাগ যা স্থায়ী হয়ে যায়, এবং এমন মডিউল যা অভ্যন্তরীণ বিবরণ লিক করে। এই জটিলতাই ডিজাইন সিদ্ধান্ত তৈরি করে—এবং এটি যে একমাত্র ধরনের আপনি ধারাবাহিকভাবে কমাতে পারবেন।
Tcl-এর শুরু একটি ব্যবহারিক লক্ষ্য দিয়ে হয়েছিল: সফটওয়্যার স্বয়ংক্রিয় করা এবং বিদ্যমান অ্যাপলিকেশনগুলো পুনর্লিখন না করে এক্সটেন্ড করা সহজ করা। জন ওস্টারহুট এটিকে এমনভাবে ডিজাইন করেছিলেন যাতে টিমগুলো “প্রচুর না কিন্তু পর্যাপ্ত প্রোগ্রামেবলিটি” সরবরাহ করতে পারে—তারপর সেই ক্ষমতাটা ব্যবহারকারীর, অপারেটরের, QA-এর বা যে কেউ স্ক্রিপ্টিং প্রয়োজন তাদের হাতে তুলে দিতে পারে।
Tcl জনপ্রিয় করেছে একটি গ্লু ল্যাঙ্গুয়েজের ধারণা: ছোট, নমনীয় স্ক্রিপ্টিং স্তর যা দ্রুততর, নিম্ন-স্তরের ভাষায় লেখা কম্পোনেন্টগুলোকে সংযুক্ত করে। প্রতিটি ফিচার মোনোলিথে বিল্ড করার পরিবর্তে, আপনি একটি কমান্ড সেট এক্সপোজ করতে পারেন এবং সেগুলোকে মিশিয়ে নতুন আচরণ গঠন করতে পারেন।
এই মডেলটি প্রভাবশালী কারণ এটি কাজ করার প্রকৃত পদ্ধতির সাথে মেলে। মানুষ কেবল প্রোডাক্ট বানায় না; তারা বিল্ড-সিস্টেম, টেস্ট হার্নেস, অ্যাডমিন টুল, ডাটা কনভার্টার, এবং একবারের অটোমেশনও বানায়। একটি হালকা স্ক্রিপ্টিং স্তর এসব কাজকে “টিকেট দিন” থেকে “একটি স্ক্রিপ্ট লিখুন” এ পরিবর্তন করে।
Tcl এমবেডিংকে একটি প্রথম-শ্রেণীর উদ্বেগ বানিয়েছে। আপনি একটি ইন্টারপ্রেটার অ্যাপে ঢুকিয়ে দিতে পারতেন, একটি পরিষ্কার কমান্ড ইন্টারফেস এক্সপোজ করতে পারতেন, এবং সঙ্গে সঙ্গে কনফিগারেবিলিটি ও দ্রুত ইটারেশন পেতেন।
এই একই প্যাটার্ন আজকে প্লাগইন সিস্টেম, কনফিগারেশন ভাষা, এক্সটেনশন API, এবং এমবেডেড স্ক্রিপ্টিং রUNTIME-এ দেখা যায়—স্ক্রিপ্ট সিনট্যাক্স Tcl-র মত না হলেও।
এটি একটি গুরুত্বপূর্ণ ডিজাইন অভ্যাসকেও জোর দিয়েছে: স্থিতিশীল প্রিমিটিভগুলো (হোস্ট অ্যাপের কোর ক্ষমতা) পরিবর্তনশীল কম্পোজিশন (স্ক্রিপ্ট) থেকে আলাদা রাখা। যখন এটি কাজ করে, টুলগুলি দ্রুত বিকাশ পায় কোরকে ক্রমাগত অস্থির না করে।
Tcl-এর সিনট্যাক্স ও “প্রতিটি কিছুই একটি স্ট্রিং” মডেল কিছু ক্ষেত্রে অস্বাভাবিক মনে হতে পারে, এবং বড় Tcl কোডবেসগুলো শক্ত নিয়মাবলী ছাড়া কঠিন হতে পারে। নতুন ইকোসিস্টেমগুলো যখন সমৃদ্ধ স্ট্যান্ডার্ড লাইব্রেরি, উন্নত টুলিং, ও বড় কমিউনিটি দিল—from টিমগুলো প্রাকৃতিকভাবেই স্থানান্তরিত হয়েছে।
তবুও Tcl-এর ঐতিহ্য মুছে যায় না: এটি প্রমাণ করেছে যে এক্সটেনসিবিলিটি ও অটোমেশন এক্সট্রা নয়—এগুলো এমন প্রোডাক্ট ফিচার যা ব্যবহারকারী ও রক্ষণাবেক্ষণকারী উভয়ের জন্য জটিলতা উল্লেখযোগ্যভাবে কমাতে পারে।
Tcl এমন একটি প্রলক্ষণীয় কঠোর ধারণার চারপাশে নির্মিত ছিল: কোর ছোট রাখো, কম্পোজিশন শক্তিশালী করো, এবং স্ক্রিপ্টগুলো পাঠযোগ্য রাখো যাতে মানুষ একে অপরের সাথে বারবার অনুবাদ ছাড়া কাজ করতে পারে।
বিশেষায়িত ফিচারের বিশাল সেট পাঠানোর পরিবর্তে, Tcl কয়েকটি প্রিমিটিভে (স্ট্রিং, কমান্ড, সহজ ইভ্যালুয়েশন রুল) নির্ভর করত এবং ব্যবহারকারীদের তাদের কম্বাইন করতে বলত।
এই দর্শন ডিজাইনারে কম ধারণা রাখার দিকে ঠেলে দেয়—যদি আপনি দুই বা তিনটি ধারাবাহিক বিল্ডিং ব্লক দিয়ে দশটি প্রয়োজন সমাধান করতে পারেন, আপনি শেখার সারফেস এরিয়া ছোট করেন।
সফটওয়্যার ডিজাইনে একটি মূল ফাঁদ হল নির্মাতার সুবিধার জন্য অপ্টিমাইজ করা। একটি ফিচার বাস্তবে সহজ হতে পারে (একটি অপশন কপি করা, বিশেষ ফ্ল্যাগ যোগ করা), কিন্তু পণ্যটিকে ব্যবহার করতে কঠিন করে তুলতে পারে।
Tcl-এর জোর ছিল বিপরীত দিকে: মানসিক মডেলটি সোজা রাখো, যদিও ইমপ্লিমেন্টেশন পেছনে বেশি কাজ করুক।
আপনি যখন কোনো প্রস্তাব পর্যালোচনা করবেন, জিজ্ঞাসা করুন: এটি ব্যবহারকারীর মনে রাখতে হবে এমন ধারণার সংখ্যা কমাচ্ছে, না কি আরেকটি ব্যতিক্রম যোগ করছে?
মিনিমালিজম তখনই সহায়ক যখন প্রিমিটিভগুলো সঙ্গতিপূর্ণ। যদি দুইটি কমান্ড দেখতে সমান কিন্তু এজ কেসে আলাদা আচরণ করে, ব্যবহারকারীরা অজানা ট্রিভিয়া মুখস্থ করতে হয়। একটি ছোট সেটের টুলও “ ধারাল ধারালো কোণা” হয়ে উঠতে পারে যখন নিয়মগুলো সূক্ষ্মভাবে ভিন্ন।
একটি রাঁধার ঘর ভাবুন: একটি ভালো ছুরি, প্যান, ও ওভেন আপনাকে অনেক রেসিপি তৈরি করতে দেয় বিভিন্ন টেকনিক মিলিয়ে। একটি এমন গ্যাজেট যা কেবল অ্যাভোকাডো কেটে—একটি একবারের ফিচার; বিক্রিতে সহজ কিন্তু ড্রয়ারগুলো জটিল করে।
Tcl-এর দর্শন ছুরি ও প্যানের পক্ষে বলছে: সাধারণ টুলগুলো যা পরিষ্কারভাবে কম্পোজ করে, যাতে প্রতি নতুন রেসিপির জন্য নতুন গ্যাজেট লাগত না।
1986 সালে, Fred Brooks একটি প্রবন্ধ লিখেছেন যার প্রলোভনমূলক উপসংহার ছিল: সফটওয়্যার ডেভেলপমেন্টকে এক ধাক্কাতেই দশগুণ দ্রুত, সস্তা, এবং আরো নির্ভরযোগ্য করে তুলতে কোন একক ব্রেকথ্রু বা “সিলভার বুলেট” নেই।
তাঁর বক্তব্য ছিল—উন্নতি অসম্ভব নয়; বরং সফটওয়্যার এমন একটি মাধ্যম যেখানে আমরা প্রায় যেকোনো কিছুই করতে পারি, এবং সেই স্বাধীনতা একটি বিশেষ বোঝা আনে: আমরা নির্মাণের সময়ই ধারাবাহিকভাবে জিনিসগুলো সংজ্ঞায়িত করছি। উন্নত টুল সাহায্য করে, কিন্তু সবচেয়ে কঠিন অংশকে মুছে দেয় না।
ব্রুকস জটিলতাকে দুই ভাগে ভাগ করেছেন:
টুলগুলো আকস্মিক জটিলতাকে চূর্ণ করতে পারে। উচ্চ-স্তরের ভাষা, ভার্সন কন্ট্রোল, CI, কন্টেইনার, ম্যানেজড ডাটাবেস, এবং ভাল IDE-র ফলে আমরা অনেক পেয়েছি। কিন্তু ব্রুকস যুক্তি দিয়েছেন যে অপরিহার্য জটিলতা প্রধান এবং তা টুল উন্নত হলেও হারিয়ে যায় না।
আধুনিক প্ল্যাটফর্মগুলো থাকা সত্ত্বেও, দলগুলো এখনও তাদের অধিকাংশ শক্তি ব্যয় করে চাহিদা দরকষাকষি, সিস্টেম ইন্টিগ্রেশন, ব্যতিক্রম হ্যান্ডেল করা, এবং আচরণ দীর্ঘমেয়াদে সঙ্গত রাখার উপর। সারফেস এরিয়া বদলে যেতে পারে (ক্লাউড API বদলে ডিভাইস ড্রাইভার), কিন্তু মূল চ্যালেঞ্জ একই থাকে: মানবিক চাহিদাকে নির্ভুল, রক্ষণযোগ্য আচরণে অনুবাদ করা।
এটি সেই টানটান পরিস্থিতি তৈরি করে যেখানে ওস্টারহুট প্রবলভাবে বলছেন: যদি অপরিহার্য জটিলতাকে মুছা না যায়, তবে কি শৃঙ্খলাবদ্ধ ডিজাইন তা অর্থবহভাবে কমাতে পারে—এবং কতটা তা দিনে দিনে ডেভেলপারদের মাথায় লিক করে না?
লোকেরা কখনও কখনও “ওস্টারহুট বনাম ব্রুকস” কে আশাবাদ বনাম বাস্তববাদের যুদ্ধ হিসেবে দেখেন। বেশি ফলপ্রসূ হবে যদি এটিকে একে অপরের সমান্তরাল বর্ণনা বলে ধরা হয়—দুটি অভিজ্ঞ ইঞ্জিনিয়ারের একই সমস্যার বিভিন্ন দিক তুলে ধরছে।
ব্রুকস-এর “No Silver Bullet” বলে একক কোনো ব্রেকথ্রু সব কষ্ট মুছবে না। ওস্টারহুট এটি অস্বীকার করেন না।
তাঁর পুশব্যাক সংকীর্ণ এবং ব্যবহারিক: দলগুলো প্রায়ই জটিলতাকে অনিবার্য মনে করে, যখন আসলে অনেক অংশ স্ব-প্ররোচিত।
ওস্টারহুটের দৃষ্টিতে, ভাল ডিজাইন জটিলতাকে তাৎপর্যপূর্ণভাবে কমাতে পারে—সফটওয়্যারকে “সহজ” বানায় না, বরং বদলানো কম বিভ্রান্তিকর করে। এবং বিভ্রান্তি হল যা দৈনন্দিন কাজকে ধীর করে তোলার বড় কারণ।
ব্রুকস যাকে অপরিহার্য কষ্ট বলে ফোকাস করে: সফটওয়্যারকে জটিল বাস্তব সমস্যাগুলো, পরিবর্তনশীল চাহিদা, এবং বাহ্যিক এজ কেসগুলো মডেল করতে হয়। চমৎকার টুল থাকা সত্ত্বেও আপনি সব জটিলতা মুছে ফেলতে পারবেন না; কেবলই পরিচালনা করতে পারবেন।
বিতর্ক দেখা যাক যতটা—তারা আসলে বেশিরভাগ জায়গায় একমত:
“কে সঠিক?” জিজ্ঞাসা করার বদলে জিজ্ঞাসা করুন: এই ক্যালেন্ডার কোয়ার্টারে কোন জটিলতাগুলো আমরা নিয়ন্ত্রণ করতে পারি?
দলগুলো বাজার পরিবর্তন বা ডোমেইনের মূল কষ্ট নিয়ন্ত্রণ করতে পারবেনা। কিন্তু তারা নিয়ন্ত্রণ করতে পারে—নতুন ফিচারগুলো বিশেষ কেস যোগ করে কি না, APIগুলো কলারকে গোপন নিয়ম মনে রাখতে বাধ্য করে কি না, মডিউলগুলো জটিলতা লুকায় কি না বা লিক করে কি না।
এটাই ব্যবহারিক মধ্যভাগ: অপরিহার্য জটিলতাকে মেনে নিন, এবং আকস্মিক ধরনের প্রতিরোধে অত্যন্ত বিচক্ষণ থাকুন।
একটি ডিপ মডিউল এমন একটি কম্পোনেন্ট যা অনেকটি কাজ করে, অথচ ছোট, সহজবোধ্য ইন্টারফেস এক্সপোজ করে। “ডেপ্থ” হচ্ছে কতটা জটিলতা মডিউলটি আপনার প্লেট থেকে সরিয়ে নেয়: কলারদের কষ্টের বিস্তারিত জানতে হবে না, এবং ইন্টারফেস তাদের বাধ্য করে না।
একটি শ্যালো মডিউল উল্টো: এটি ছোট লজিক র্যাপ করলেও জটিলতাকে বহির্গামী করে দেয়—অনেক প্যারামিটার, বিশেষ ফ্ল্যাগ, নির্দিষ্ট কল অর্ডার, বা "আপনি মনে রাখবেন" নিয়মের মাধ্যমে।
একটি রেস্তোরাঁ চিন্তা করুন। একটি ডিপ মডিউল হলো রান্নাঘর: আপনি একটি সহজ মেনু থেকে “পাস্তা” অর্ডার করবেন এবং সাপ্লায়ার পছন্দ, ফুটানোর সময়, বা প্লেটিং সম্পর্কে চিন্তা করবেন না।
একটি শ্যালো মডিউল হলো এমন একটি “কিচেন” যা আপনাকে কাঁচা উপকরণ দিয়ে ১২-ধাপের নির্দেশ দেয় এবং আপনার নিজে প্যান আনতে বলে। কাজ এখনও হচ্ছে—কিন্তু তা কাস্টমারের উপর চলে এসেছে।
অতিরিক্ত লেয়ার ভাল যখন তারা বহু সিদ্ধান্তকে একটি স্পষ্ট পছন্দে কনকিউজ করে।
উদাহরণস্বরূপ, একটি স্টোরেজ লেয়ার যা save(order) এক্সপোজ করে এবং অভ্যন্তরীণভাবে রাইট্রাই, সিরিয়ালাইজেশন, এবং ইনডেক্সিং হ্যান্ডল করে—এটি ডিপ।
লেয়ারগুলো ক্ষতিকর যখন এগুলো কেবল নাম বদলে দেয় বা অপশন যোগ করে। যদি নতুন অ্যাবস্ট্রাকশনটি বেশিরভাগ কনফিগেশন যোগ করে—যেমন save(order, format, retries, timeout, mode, legacyMode)—তাহলে এটি সম্ভবত শ্যালো। কোড “অর্গানাইজড” দেখতেই পারে, কিন্তু কগনিটিভ লোড প্রতিটি কল সাইটে প্রকাশ পায়।
useCache, skipValidation, force, legacy।ডিপ মডিউল কেবল কোড এনক্যাপসুলেট করে না—এটি সিদ্ধান্তও এনক্যাপসুলেট করে।
“ভাল” API মানে কেবল ক্ষমতাশালী নয়; এটি মানে মানুষদের মাথায় সহজে ধারণাযোগ্য।
ওস্টারহুটের ডিজাইন লেন্স আপনাকে API-কে মানসিক প্রচেষ্টার দ্বারা বিচার করতে বলবে: আপনাকে কতগুলো নিয়ম মনে রাখতে হয়, কতগুলো ব্যতিক্রম অনুমান করতে হয়, এবং ভুলভাবে করার সম্ভাবনা কতটা।
মানুষ-বান্ধব API জামানতভাবে ছোট, সঙ্গতিপূর্ণ, এবং দুর্ব্যবহার কঠিন হয়।
ছোট মানে ক্ষমতাহীন নয়—এটি সারফেস এরিয়া কয়েকটি ধারণায় ঘনত্ব করা। সঙ্গতিপূর্ণ মানে একই প্যাটার্ন পুরো সিস্টেম জুড়ে কাজ করে (প্যারামিটার, এরর হ্যান্ডলিং, নামকরণ, রিটার্ন টাইপ)। দুর্ব্যবহার কঠিন মানে API আপনাকে নিরাপদ পথেই গাইড করে: স্পষ্ট ইনভারিয়েন্ট, বাউন্ডারিতে ভ্যালিডেশন, এবং টাইপ বা রানটাইম চেক যা দ্রুত ব্যর্থ হয়।
প্রতিটি অতিরিক্ত ফ্ল্যাগ/মোড/“কেবল কেসটি” সকল ব্যবহারকারীর উপর কর আরোপ করে। যদিও মাত্র ৫% কলারকে এটি লাগে, ১০০% কলার এখন জানে যে এটি আছে, ভাববে তাদের কি এটি দরকার, এবং অন্যান্য অপশনগুলোর সাথে ইন্টারঅ্যাকশনের ক্ষেত্রে আচরণ ব্যাখ্যা করবে।
এভাবেই API-গুলো গুপ্ত জটিলতা জমায়: না কোনও একটি কলেই, বরং কম্বিনেটরিক্সে।
ডিফল্ট মানুষকে দয়া করে: এগুলো অধিকাংশ কলারকে সিদ্ধান্ত বাদ দিতে দেয় এবং তবুও যুক্তিযুক্ত আচরণ দেয়। কনভেনশন (একটি স্পষ্ট উপায়) ব্যবহারকারীর মনে শাখা কমায়। নামকরণও বাস্তব কাজ করে: ক্রিয়া ও নাম এমন রাখুন যা ব্যবহারকারীর ইচ্ছার সাথে মেলে, এবং একই রকম অপারেশনগুলোর নাম একইভাবে রাখুন।
আরেকটি অনুস্মারক: অভ্যন্তরীণ API-ও পাবলিক API-এর মতোই গুরুত্বপূর্ণ। পণ্য জটিলতার অধিকাংশ অংশই ব্যাকগ্রাউন্ডে থাকে—সার্ভিস বাউন্ডারি, শেয়ারড লাইব্রেরি, এবং “হেল্পার” মডিউল। ঐ ইন্টারফেসগুলোকে একটি পণ্য হিসেবে বিবেচনা করুন, রিভিউ ও ভার্সনিং শৃঙ্খলা সহ (দেখুন /blog/deep-modules)।
জটিলতা সাধারণত একক “বদ সিদ্ধান্ত” হিসেবে আসে না। এটি ছোট, যুক্তিযুক্ত-দেখানো প্যাঁচের মাধ্যমে জমা হয়—বিশেষত যখন দলগুলো ডেডলাইন চাপের মধ্যে থাকে এবং তাত্ক্ষণিক লক্ষ্য হল শিপ করা।
একটি ফাঁদ হল ফিচার ফ্ল্যাগগুলো সর্বত্র। ফ্ল্যাগ নিরাপদ রোলআউটের জন্য অপরিহার্য, কিন্তু যখন সেগুলো দীর্ঘকাল বিদ্যমান থাকে, প্রতিটি ফ্ল্যাগ ভিন্ন ভিন্ন আচরণ সম্ভাবনাকে বহুগুণ করে দেয়। ইঞ্জিনিয়াররা “সিস্টেম” সম্পর্কে ভাবা বন্ধ করে দিয়ে “সিস্টেম, তবে ফ্ল্যাগ A চালু হলে এবং ব্যবহারকারী সেগমেন্ট B-এ থাকলে”—এই রকম চিন্তা করতে শুরু করে।
অন্যটি হলো বিশেষ-কেস লজিক: “এন্টারপ্রাইজ গ্রাহকরা X চাইবে”, “অঞ্চল Y-তে ব্যতিক্রম”, “অ্যাকাউন্ট ৯০ দিনের বেশি পুরনো হলে আলাদা আচরণ”। এই ব্যতিক্রমগুলো কোডবেস জুড়ে ছড়িয়ে পড়ে, এবং কয়েক মাস পর কেউ জানে না কোনগুলো এখনও প্রয়োজন।
তৃতীয় হলো লিাকিং অ্যাবস্ট্রাকশন। একটি API যদি কলারদের অভ্যন্তরীণ বিবরণ (টাইমিং, স্টোরেজ ফরম্যাট, ক্যাশিং নিয়ম) বুঝতে বাধ্য করে, জটিলতাটি বাহিরে চলে আসে। এক মডিউল যে বোঝা বহন করত তা প্রতিটি কলারে ছড়িয়ে পড়ে।
ট্যাকটিক্যাল প্রোগ্রামিং এই সপ্তাহের জন্য অপ্টিমাইজ করে: দ্রুত ফিক্স, ন্যূনতম পরিবর্তন, “শিপ করো।”
স্ট্র্যাটেজিক প্রোগ্রামিং আগামী বছরের জন্য অপ্টিমাইজ করে: ছোট রিডিজাইন যা একই ধরণের বাগ প্রতিহত করে এবং ভবিষ্যৎ কাজ কমায়।
বিপদটা হল “মেইনটেনেন্স ইন্টারেস্ট”। একটি দ্রুত ওয়ার্কঅ্যারাউন্ড এখন সস্তা মনে হয়, কিন্তু আপনি সুদ হিসেবে পরিশোধ করবেন: ধীর অনবোর্ডিং, ভঙ্গুর রিলিজ, এবং ভয়-চালিত ডেভেলপমেন্ট যেখানে কেউ পুরনো কোডটিতে হাত দিতে চায় না।
কোড রিভিউতে হালকা প্রম্পট যোগ করুন: “এটি কি একটি নতুন বিশেষ কেস যোগ করছে?” “API কি এই বিবরণটি লুকাতে পারবে?” “আমরা কোন জটিলতা রেখে যাচ্ছি?”
অপ্রয়োজনীয় সিদ্ধান্তের জন্য সংক্ষিপ্ত রেকর্ড রাখুন (কয়েকটি বুলেট যথেষ্ট)। এবং প্রতিটি স্প্রিন্টে একটি ছোট রিফ্যাক্টর বাজেট রাখুন যাতে স্ট্র্যাটেজিক ফিক্সগুলো এক্সট্রাক্রিকুলার কাজ না হয়ে যায়।
জটিলতা ইঞ্জিনিয়ারিংয়ে আটকিয়ে থাকে না। এটি শিডিউল, নির্ভরযোগ্যতা, এবং গ্রাহকের অভিজ্ঞতায় ভেসে যায়।
যখন সিস্টেম বোঝা কঠিন, প্রতিটি পরিবর্তন নিতে বেশি সময় লাগে। টাইম-টু-মার্কেট পিছিয়ে যায় কারণ প্রতিটি রিলিজে বেশি সমন্বয়, বেশি রিগ্রেশন টেস্টিং, এবং আরো সতর্ক রিভিউ দরকার হয়।
নির্ভরযোগ্যতাও ক্ষতিগ্রস্ত হয়। জটিল সিস্টেম এমন ইন্টারঅ্যাকশন তৈরি করে যেগুলো কেউ পুরোপুরি পূর্বানুমান করতে পারে না, তাই বাগগুলো থাকে এজ কেসগুলো হিসেবে: কেবল যখন কুপন, সেভড কার্ট, এবং আঞ্চলিক ট্যাক্স রুল একসঙ্গে আসে তখন চেকআউট ব্যর্থ হয়। এধরনের ইন্সিডেন্টগুলো পুনরুত্পাদন করা কঠিন এবং ঠিক করতে ধীর।
অনবোর্ডিং একটি লুকানো আঁটকে পরিণত হয়। নতুন সহকর্মীরা ব্যবহারিক মানসিক মডেল তৈরি করতে পারে না, তাই তারা ঝুঁকিপূর্ণ এলাকা এড়ায়, এমন প্যাটার্ন কপি করে যা তারা বোঝে না, এবং অজান্তেই জটিলতা বাড়ায়।
গ্রাহকরা মানে করে না আচরণ কোনো “বিশেষ কেস” কোড থেকে আসে কি না। তারা এটিকে অভিজ্ঞতা হিসেবে দেখে: কনফিগারেশন যা সব জায়গায় প্রযোজ্য নয়, ফ্লো যা আপনি কীভাবে এসেছেন তার ওপর নির্ভর করে বদলে যায়, এমন ফিচার যা “অধিকাংশ সময় কাজ করে।”
এটা আস্থা কমায়, চার্ন বাড়ায়, এবং গ্রহণ দ্রুত বন্ধ করে দেয়।
সাপোর্ট টিমগুলো জটিলতার জন্য দীর্ঘ টিকিট এবং আরো ব্যাক-এন্ড যোগাযোগে ভুগে। অপারেশনস অ্যার্টি করে বেশি অ্যালার্ট, বেশি রানবুক, এবং আরো সাবধানে ডিপ্লয়মেন্ট। প্রতিটি ব্যতিক্রম মনিটর, ডকুমেন্ট, এবং ব্যাখ্যা করার কিছু হয়ে যায়।
একটি “আরেকটি নোটিফিকেশন রুল” চাওয়া কল্পনা করুন। যোগ করা দ্রুত মনে হলেও, এটি আচরণের আরেকটি শাখা, আরো UI কপি, আরো টেস্ট কেস, এবং ব্যবহারকারীর ভুল কনফিগারেশনের আরো উপায় যোগ করে।
এখন তুলনা করুন বিদ্যমান নোটিফিকেশন ফ্লো সরল করার সঙ্গে: কম রুল টাইপ, স্পষ্ট ডিফল্ট, এবং ওয়েব ও মোবাইলে সঙ্গত আচরণ। আপনি কম নকশার নক-নব যোগ করতে পারেন, কিন্তু আপনি বিস্ময় কমান—পণ্যটি ব্যবহার করা সহজ হয়, সাপোর্ট সহজ হয়, এবং দ্রুত বিকাশ করা যায়।
কর্মদক্ষতা, সিকিউরিটি বা পারফরম্যান্সের মত জটিলতাকেও পরিকল্পনা করুন, মাপুন, এবং রক্ষা করুন। যদি আপনি কেবল তখনই জটিলতা লক্ষ্য করেন যখন ডেলিভারি ধীর হয়ে যায়, আপনি ইতিমধ্যেই সুদ দিতে শুরু করে দিয়েছেন।
ফিচার স্কোপের পাশাপাশি নির্ধারণ করুন কতটা নতুন জটিলতা একটি রিলিজ যোগ করতে পারবে। বাজেট সরল হতে পারে: “নতুন ধারণা যোগ করা যাবে না যদি না আমরা একটা মুছে ফেলি,” বা “যেকোনো নতুন ইন্টিগ্রেশন পুরনো একটি পথ প্রতিস্থাপন করবে।”
পরিকল্পনায় ট্রেডঅফগুলো স্পষ্ট করুন: যদি একটি ফিচারের জন্য তিনটি নতুন কনফিগ মোড এবং দুইটি ব্যতিক্রম লাগে, সেটি এমন একটি ফিচারের তুলনায় বেশি ‘খরচ’ করা উচিত যা বিদ্যমান ধারণাগুলোতে ফিট করে।
আপনাকে নিখুঁত সংখ্যা দরকার নেই—শুধু সঠিক দিক নির্দেশ করে এমন সংকেত:
এগুলো রিলিজপ্রতি ট্র্যাক করুন, এবং সিদ্ধান্তগুলোর সাথে যুক্ত করুন: “আমরা দুইটি নতুন পাবলিক অপশন যোগ করেছি; বদলে আমরা কি কিছু মুছেছি বা সরল করেছি?”
প্রোটোটাইপগুলো প্রায়শই “তাই কি আমরা এটা বানাতে পারি?” দিয়ে বিচার করা হয়। পরিবর্তে সেগুলোকে প্রশ্ন করুন: “এটি ব্যবহার করতে কেমন লাগছে এবং ভুলভাবে ব্যবহার করা কঠিন কি না?”
কাউকে যে ফিচারটি অপরিচিত তাকে প্রোটোটাইপ দিয়ে বাস্তব কাজ করান। টাইম-টু-সাকসেস, জিজ্ঞাসা করা প্রশ্ন, এবং যেখানে তারা ভুল ধারণা করে—এসব মাপুন। সেগুলোই জটিলতা-হটস্পট।
এটাই এমন জায়গা যেখানে আধুনিক বিল্ড ওয়ার্কফ্লো আকস্মিক জটিলতা কমাতে পারে—যদি তারা ইটারেশনকে টাইট রাখে এবং ভুলগুলো রোলব্যাক করা সহজ করে। উদাহরণস্বরূপ, যখন টিমগুলো একটি ভাইব-কোডিং প্ল্যাটফর্ম যেমন Koder.ai ব্যবহার করে একটি ইন্টারনাল টুল বা নতুন ফ্লো চ্যাটের মাধ্যমে স্কেচ করে, তখন planning mode (উদ্দেশ্য পরিস্কার করার জন্য) এবং snapshots/rollback (ঝুঁকিভরা পরিবর্তন দ্রুত উল্টে দেওয়ার সুবিধা) মত ফিচারগুলো প্রথমিক পরীক্ষাকে নিরাপদ করে—অর্ধবিকৃত অ্যাবস্ট্রাকশনে কম বেঁধে পড়ে। যদি প্রোটোটাইপ গ্র্যাজুয়েট করে, আপনি সোর্স কোড এক্সপোর্ট করে একই “ডিপ মডিউল” এবং API শৃঙ্খলা প্রয়োগ করতে পারেন।
“জটিলতা ক্লিনআপ” কাজটি নিয়মিত করুন (ত্রৈমাসিক বা প্রতিটি বড় রিলিজে), এবং নির্ধারণ করুন “ডান কি”:
উদ্দেশ্য কেবল পরিষ্কার কোড নয়—কম ধারণা, কম ব্যতিক্রম, এবং নিরাপদ পরিবর্তন।
নিচে কিছু সরল পদক্ষেপ যা ওস্টারহুটের “জটিলতা শত্রু” ভাবনাকে সপ্তাহ-প্রতি-সপ্তাহের টিম অভ্যাসে রূপান্তর করে।
একটি উপ-সিস্টেম বেছে নিন যা নিয়মিত বিভ্রান্তি সৃষ্টি করে (অনবোর্ডিং যে অংশে সমস্যা, পুনরাবৃত্ত বাগ, বা অনেক “কিভাবে কাজ করে?” প্রশ্ন)।
داখুন
ইন্টারনাল ফলোআপ যা আপনি চালাতে পারেন: পরিকল্পনায় একটি “complexity review” (/blog/complexity-review) এবং দ্রুত পরীক্ষা করে নিন আপনার টুলিং আকস্মিক জটিলতা কমাচ্ছে নাকি নতুন স্তর যোগ করছে (/pricing)।
আপনি যদি এই সপ্তাহে কেবল একটি বিশেষ কেস মুছতে পেতেন, তাহলে প্রথমে আপনি কোনটিকে মুছে ফেলতেন?
জটিলতা হল যে ফাঁক যা আপনার প্রত্যাশা এবং সিস্টেমের বাস্তব আচরণের মধ্যে পড়ে যখন আপনি কোনো পরিবর্তন করেন।
আপনি এটি অনুভব করেন যখন ছোট পরিবর্তনগুলো ঝুঁকিপূর্ণ মনে হয় কারণ আপনি বিস্তারের (blast radius) ভবিষ্যদ্বাণী করতে পারেন না — পরীক্ষা, সার্ভিস, কনফিগ, গ্রাহক বা ভিন্ন এজ কেসগুলো কীভাবে প্রভাবিত হবে তা অনিশ্চিত থাকে।
রিজনিং জটিল হওয়ার কয়েকটি সংকেত দেখুন:
Essential complexity ডোমেন থেকেই আসে (বিধি-বিধান, বাস্তব বিশ্বের এজ কেস, ব্যবসায়িক নিয়ম)। এগুলো মুছে ফেলা যায় না—শুধু ভালভাবে মডেল করতে হয়।
Accidental complexity স্ব-প্ররোচিত: লিাকিং অ্যাবস্ট্রাকশন, নকল করা লজিক, অনেক মোড/ফ্ল্যাগ, অস্পষ্ট API। দলগুলো আইনগতভাবে এই অংশটি ডিজাইন ও সরলীকরণ করে কমাতে পারে।
একটি ডিপ মডিউল অনেক কাজ করে কিন্তু একটি ছোট, সহজ-ধরার ইন্টারফেস দেয়। এটি গৃহীত জটিলতা (রাইট্রাই, সিরিয়ালাইজেশন, অর্ডারিং, ইনভারিয়েন্ট) নিজের মধ্যে রাখে যাতে কলারদের জানতে না হয়।
প্র্যাকটিক্যাল টেস্ট: যদি বেশিরভাগ কলার ওই মডিউল সঠিকভাবে ব্যবহার করতে পারে অভ্যন্তরীণ নিয়ম না জেনেই, এটি ডিপ; যদি কলারদের নিয়ম ও সিকোয়েন্স মনে রাখতে হয়, এটি শ্যালো।
সাধারণ লক্ষণগুলো:
legacy, skipValidation, force, mode)।এমন API পছন্দ করুন যা:
“আরেকটি অপশন যোগ করা” প্রলোভনে পড়ে প্রথমেই জিজ্ঞেস করুন—সব কলারের জন্য কি এই সিদ্ধান্তটি চিন্তা করা উচিত, নাকি আমরা ইন্টারফেসটা এমন সাজাতে পারি যাতে অধিকাংশ কলারের জন্য সিদ্ধান্তটা অপ্রয়োজনীয় হয়?
ফিচার ফ্ল্যাগগুলো নিয়ন্ত্রিত রোলআউটের জন্য ব্যবহার করুন, তারপর এটিকে একটি ঋণ হিসেবে বিবেচনা করুন যার একটি সমাপ্তির তারিখ আছে:
দীর্ঘকালীন ফ্ল্যাগগুলো ইঞ্জিনিয়ারদের জন্য “কতগুলি সিস্টেম” নিয়ে চিন্তা বাড়িয়ে দেয়।
পরিকল্পনায় জটিলতাকে স্পষ্ট করুন, কেবল কোড রিভিউয়ে নয়:
লক্ষ্য হল জটিলতার ট্রেডঅফগুলো প্রকাশ্যে আনা যেন তা সংস্থাভিত্তিক না হয়ে যায়।
ট্যাকটিক্যাল প্রোগ্রামিং এই সপ্তাহের জন্য অপ্টিমাইজ করে: দ্রুত প্যাচ, ন্যূনতম পরিবর্তন, “শিপ করুন”।
স্ট্র্যাটেজিক প্রোগ্রামিং আগামী বছরের জন্য অপ্টিমাইজ করে: ছোট রিডিজাইন যা পুনরাবৃত্ত সমস্যাগুলো মুছে দেয় এবং ভবিষ্যৎ কাজ কমায়।
কঠোর হিউরিস্টিক: যদি কোনো ফিক্সে কলারের জ্ঞান দরকার হয় ("প্রথমে X কল করবে" বা "প্রোডে শুধু এই ফ্ল্যাগ সেট করবেন"), তাহলে সম্ভবত মডিউলের ভিতরে সেই জটিলতাকে লুকানোর জন্য একটি স্ট্র্যাটেজিক পরিবর্তন প্রয়োজন।
Tcl-এর টেকসই পাঠ হলো: শক্তিশালী কম্পোজিশনের সাথে একটি ছোট প্রিমিটিভ সেট কতটা শক্তিশালী হতে পারে—প্রধানত এমবেডেড “গ্লু” স্তর হিসেবে।
আধুনিক সমতুল্যগুলোর মধ্যে আছে:
ডিজাইন লক্ষ্য একই: কোরকে সাদাসিধে ও স্থিতিশীল রাখুন, পরিবর্তনগুলো পরিষ্কার ইন্টারফেসের মাধ্যমে ঘটুক।
শ্যালো মডিউল প্রায়ই “সজ্জিত” দেখায় কিন্তু জটিলতাকে প্রতিটি কলারে পাঠিয়ে দেয়।