System Design Tradeoffs (ট্রেড-অফ ডিপ-ডাইভ)
সিস্টেম ডিজাইনে কোনো "নিখুঁত" সমাধান নেই; আছে শুধু ট্রেড-অফ। এক জায়গায় সুবিধা পেতে হলে অন্য জায়গায় কিছু স্যাক্রিফাইস করতে হয়। ইন্টারভিউতে এই ট্রেড-অফগুলো বুঝতে পারা এবং ব্যাখ্যা করা সবচেয়ে বেশি গুরুত্বপূর্ণ।
১. ভার্টিক্যাল বনাম হরিজন্টাল স্কেলিং (Vertical vs Horizontal Scaling)
| ফিচার | Vertical Scaling (Up) | Horizontal Scaling (Out) |
|---|---|---|
| পদ্ধতি | একটি সার্ভারের পাওয়ার বাড়ানো (RAM/CPU) | মেম্বার অফ সার্ভার বাড়ানো |
| সুবিধা | ইমপ্লিমেন্ট করা সহজ, কোনো ডেটা শার্ডিং লাগে না | অসীম স্কেলেবল, হাইলি এভেইল্যাবল |
| সীমাবদ্ধতা | হার্ডওয়্যারের একটি ফিক্সড লিমিট আছে | ডিস্ট্রিবিউটেড সিস্টেমের জটিলতা বাড়ে |
| SPOF | একটি সার্ভার ডাউন হলে সব শেষ | একটি ডাউন হলে অন্যরা কাজ চালিয়ে নেয় |
২. কনকারেন্সি বনাম প্যারালালিজম (Concurrency vs Parallelism)
- Concurrency: একসাথে একাধিক কাজ ম্যানেজ করা। একটি কোর (Core) দ্রুত সুইচ করে একাধিক থ্রেড হ্যান্ডেল করে। এটি মূলত "Dealing with lots of things at once"।
- Parallelism: একসাথে একাধিক কাজ সরাসরি করা। একাধিক কোর (Core) বা প্রসেসর ব্যবহার করে একই সময়ে ভিন্ন ভিন্ন কাজ করা হয়। এটি মূলত "Doing lots of things at once"।
৩. লং পোলিং বনাম ওয়েব-সকেট (Long Polling vs WebSockets)
- Long Polling: ক্লায়েন্ট রিকোয়েস্ট পাঠায় এবং সার্ভার ডেটা না আসা পর্যন্ত কানেকশন ধরে রাখে। ডেটা পাওয়ার পর কানেকশন শেষ হয় এবং ক্লায়েন্ট আবার নতুন রিকোয়েস্ট দেয়। (মেসেজ ডেলিভারিতে দেরি হতে পারে)।
- WebSockets: এটি একটি কন্টিনিউয়াস কানেকশন। ক্লায়েন্ট এবং সার্ভার উভয়েই যেকোনো সময় ডেটা পাঠাতে পারে। (রিয়েল-টাইম অ্যাপের জন্য সেরা)।
৪. স্টেটফুল বনাম স্টেটলেস (Stateful vs Stateless Architecture)
- Stateful: সার্ভার ইউজারের সেশন বা ডেটা মেমরিতে মনে রাখে। এতে পরবর্তী রিকোয়েস্টগুলো একই সার্ভারে পাঠাতে হয় (Sticky Session)। স্কেলিং করা কঠিন।
- Stateless: সার্ভার ইউজারের কোনো তথ্য মনে রাখে না। প্রতিটি রিকোয়েস্টই ইন্ডিপেনডেন্ট। ইউজার যেকোনো সার্ভারে হিট করতে পারে। এটি স্কেলিংয়ের জন্য আদর্শ।
৫. স্ট্রং বনাম ইভেন্টুয়াল কনসিস্টেন্সি (Strong vs Eventual Consistency)
- Strong Consistency: ডেটা আপডেট হওয়ার সাথে সাথে সব ইউজার লেটেস্ট ডেটা দেখতে পাবে। (উদা: ব্যাংক ব্যালেন্স)। ল্যাটেন্সি বেশি হয়।
- Eventual Consistency: ডেটা আপডেট হওয়ার পর সব ইউজার লেটেস্ট ডেটা পেতে কিছুটা সময় (মিলি-সেকেন্ড থেকে সেকেন্ড) লাগতে পারে। (উদা: ফেসবুক লাইক বা পোস্ট)। ল্যাটেন্সি কম হয়।
৬. পুশ বনাম পুল আর্কিটেকচার (Push vs Pull Architecture)
- Pull: ক্লায়েন্ট বা কনজিউমার নির্দিষ্ট সময় পর পর সার্ভার থেকে ডেটা চেয়ে নেয়।
- Push: যখনই সার্ভারে নতুন ডেটা আসে, সার্ভার নিজে থেকে ক্লায়েন্টের কাছে ডেটা পাঠিয়ে দেয় (যেমন নোটিফিকেশন)।
৭. মনোলিথ বনাম মাইক্রোসার্ভিস (Monolith vs Microservices)
- Monolith: পুরো অ্যাপ্লিকেশন একটি সিঙ্গেল কোডবেস এবং সার্ভার হিসেবে চলে। ছোট প্রজেক্টের জন্য সহজ কিন্তু স্কেল করা এবং মেইনটেইন করা কঠিন।
- Microservices: অ্যাপ্লিকেশনটি ছোট ছোট ইন্ডিপেনডেন্ট সার্ভিসে ভাগ করা থাকে যা নেটওয়ার্কের মাধ্যমে কথা বলে। স্কেল করা এবং আলাদা টেক স্ট্যাক ব্যবহার করা সহজ কিন্তু সিস্টেমের জটিলতা বাড়ে।
৮. সাধারণ ইন্টারভিউ প্রশ্নোত্তর (General Q&A)
প্রশ্ন ১: REST এবং GraphQL এর মধ্যে ট্রেড-অফ কী?উত্তর: REST সহজ এবং স্ট্যান্ডার্ড, কিন্তু এতে অনেক সময় প্রয়োজনীয় ডেটার চেয়ে বেশি ডেটা (Over-fetching) চলে আসে। GraphQL এ ইউজার শুধু দরকারি ডেটাই পেতে পারে, যা ব্যান্ডউইথ বাঁচায়, কিন্তু এটি ইমপ্লিমেন্ট করা এবং ক্যাশ করা কিছুটা জটিল।
প্রশ্ন ২: সিঙ্ক্রোনাস এবং অ্যাসিঙ্ক্রোনাস কমিউনিকেশনের মধ্যে কোনটি কখন ভালো?উত্তর: যখন সাথে সাথে রেসপন্স দরকার (উদা: লগইন), তখন সিঙ্ক্রোনাস। আর যখন টাস্কটি সময়সাপেক্ষ (উদা: ইমেজ প্রসেসিং), তখন অ্যাসিঙ্ক্রোনাস কিউ ব্যবহার করা ভালো।
প্রশ্ন ৩: 'Push' আর্কিটেকচারের প্রধান চ্যালেঞ্জ কী?উত্তর: পুশ আর্কিটেকচারে (উদা: WebSockets) সার্ভারকে অনেকগুলো কানেকশন ওপেন রাখতে হয়, যা মেমরি এবং সিপিপিইউ-এর ওপর অনেক চাপ সৃষ্টি করে।
প্রশ্ন ৪: ডিস্ট্রিবিউটেড পেমেন্ট সিস্টেমে আপনি স্ট্রং কনসিস্টেন্সি কেন বেছে নেবেন?উত্তর: কারণ ডিস্ট্রিবিউটেড পেমেন্টে যদি ইভেন্টুয়াল কনসিস্টেন্সি থাকে, তবে একই টাকা দুইবার খরচ (Double spending) বা ব্যালেন্স আপডেটে এরর হওয়ার ঝুঁকি থাকে।
৯. সিনারিও ভিত্তিক প্রশ্ন (Scenario-based Questions)
সিনারিও ১: "আপনি একটি স্টক ট্রেডিং অ্যাপ বানাচ্ছেন। আপনি স্ট্রং না কি ইভেন্টুয়াল কনসিস্টেন্সি বেছে নেবেন?"
সমাধান: এখানে Strong Consistency ক্যাটাগরি বেছে নিতে হবে। কারণ শেয়ারের দাম একদম সঠিক না হলে ইউজারের বড় আর্থিক ক্ষতি হতে পারে। ল্যাটেন্সি বাড়লেও ডেটার নির্ভুলতা এখানে বেশি গুরুত্বপূর্ণ।
সিনারিও ২: "আপনার সিস্টেমের ইউজার হঠাৎ অনেক বেড়ে গেছে, কিন্তু আপনার কোডবেস অনেক পুরনো এবং ডিস্ট্রিবিউটেড সিস্টেম সাপোর্ট করবে না। আপনি কীভাবে স্কেল করবেন?"
সমাধান: এই পরিস্থিতিতে প্রথমে Vertical Scaling করে সার্ভারের পাওয়ার বাড়াব। কারণ হরিজন্টাল স্কেলিং করার জন্য কোডে অনেক পরিবর্তন এবং ডিস্ট্রিবিউটেড আর্কিটেকচার প্রয়োজন। তবে লং-টার্মে আমাকে হরিজন্টাল স্কেলিংয়ের দিকেই যেতে হবে।
সিনারিও ৩: "একটি নোটিফিকেশন সার্ভিসের জন্য পুশ না কি পুল—কোনটি ভালো?"
সমাধান: Push Architecture ভালো। কারণ ইউজার জানে না কখন নোটিফিকেশন আসবে। যদি ইউজার বারবার সার্ভারে "নতুন নোটিফিকেশন আছে কি না" জিজ্ঞাসা করে (Pull), তবে সার্ভারের ওপর অনেক ফালতু লোড পড়বে।
IMPORTANT
ইন্টারভিউতে "It depends" কথাটি খুব পাওয়ারফুল। আপনার ডিসিশন কেন নিচ্ছেন এবং তার বিপরীত দিকে কী কী সমস্যা হতে পারে, তা স্পাই করুন।