এই পর্বে যা শিখবেন
- SOLID-এর প্রতিটি নীতি একটি smell আর তার fix দিয়ে বলা — LSP-র ফাঁদ আর DIP, DI ও IoC-এর পার্থক্যসহ
- কোডে design pattern চেনা, কাছাকাছি pattern-গুলোর পার্থক্য বলা, আর কখন pattern নয় তা যুক্তি দিয়ে বলা
- একটি ছোট snippet review করে নির্দিষ্ট, কারণসহ, লেবেল দেওয়া মন্তব্য লেখা, আর আচরণ না বদলে refactor করা
- Test-এর স্তর, test double, TDD, coverage-এর সীমা, flaky test আর legacy কোডের প্রশ্নে বাস্তব উত্তর দেওয়া; architecture আর process-এর trade-off বলা
- STAR পদ্ধতিতে আচরণগত প্রশ্নের গোছানো উত্তর, ইন্টারভিউয়ারকে ভালো প্রশ্ন, live coding আর take-home-এর আচরণ — আর red flag বাক্য এড়ানো
১ · Software engineering ইন্টারভিউতে কী দেখা হয় — তিন স্তর আর উত্তরের ছক
Developer পদের ইন্টারভিউ সাধারণত কয়েকটি ভাগে হয়: কোডিং বা algorithm, কখনো system design, আর এই পাতার বিষয় — software engineering-এর অনুশীলন: কোড কীভাবে সাজাবেন, কীভাবে test করবেন, দলে কীভাবে কাজ করবেন। ভাগগুলোর নাম, ক্রম আর গুরুত্ব প্রতিষ্ঠানভেদে অনেক আলাদা; কোথাও একটি আলাপেই সব, কোথাও আলাদা আলাদা দিনে। নিচের প্রশ্নগুলো এ ধরনের আলোচনায় প্রায়ই আসা ধাঁচের — কোনো নির্দিষ্ট প্রতিষ্ঠানের প্রশ্ন বা প্রক্রিয়া নয়।
ইন্টারভিউয়ার সংজ্ঞা মুখস্থ শুনতে চান না; জানতে চান কোনো নীতি কখন খাটে আর কখন বাড়াবাড়ি। তাই শক্তিশালী উত্তরে প্রায় সবসময় থাকে একটি ছোট বাস্তব উদাহরণ, একটি দাম আর একটি “কখন নয়”।
| স্তর | সাধারণত যা শোনা হয় | কঠিনতার চিহ্ন |
|---|---|---|
| শুরু | সঠিক সংজ্ঞা, একটি ছোট কোডের উদাহরণ, একটি সুবিধা আর একটি অসুবিধা। নিজের পড়াশোনা বা ছোট project থেকে একটি ঘটনা। | (সহজ) |
| মাঝারি | কাছাকাছি ধারণার তফাত (Strategy বনাম State, mock বনাম stub), কোডে smell চেনা আর fix, আর follow-up-এ এক স্তর নিচে নামা। | (মাঝারি) |
| অভিজ্ঞ | কখন নীতি ভাঙা ঠিক, দলের স্তরের সিদ্ধান্ত (debt, architecture, incident), মাপ দিয়ে যাচাই, আর অন্যকে শেখানো বা বোঝানোর অভিজ্ঞতা। | (কঠিন) |
শক্তিশালী উত্তরের ছক
- প্রশ্ন পরিষ্কার করা (clarifying question): “কোন ভাষায় উত্তর চান?”, “এটি কি অনেক দিন চলবে এমন কোড, না এক সপ্তাহের prototype?” — নকশার প্রায় সব প্রশ্নের উত্তর এতে বদলায়।
- জোরে ভাবা (think aloud): অনুমান মুখে বলুন — “ধরছি payment-এর তিনটি provider আছে আর চতুর্থটি আসবে” — যাতে ইন্টারভিউয়ার ভুল অনুমান ধরিয়ে দিতে পারেন।
- নিজের অভিজ্ঞতা: “একবার আমাদের ছোট project-এ…” — একটি সত্যিকারের ছোট ঘটনা দশটি সংজ্ঞার চেয়ে বেশি বিশ্বাসযোগ্য। বানানো ঘটনা follow-up-এ ধরা পড়ে।
- Follow-up-এর জন্য তৈরি থাকা (follow-up): প্রতিটি উত্তরের শেষ বাক্য পরের প্রশ্ন ডেকে আনে। যে দিকে আপনি গভীরে যেতে পারেন, সেই দরজাটিই খোলা রাখুন।
একজন দক্ষ দর্জিকে জিজ্ঞেস করুন, “ঢিলা কাটিং ভালো, না আঁটসাঁট?” নতুন দর্জি বলবেন, “আঁটসাঁট এখন চলে।” অভিজ্ঞ দর্জি আগে জিজ্ঞেস করবেন, “কোথায় পরবেন? গরমে না শীতে? প্রতিদিন না অনুষ্ঠানে?” তারপর বলবেন, “অফিসে প্রতিদিনের জন্য একটু ঢিলা, নইলে বসতে কষ্ট; তবে অনুষ্ঠানের পাঞ্জাবি আঁটসাঁট মানায়।” তিনি মাপ নিয়েছেন, সিদ্ধান্ত দিয়েছেন, দামও বলেছেন। “SOLID মানা উচিত কি না” — এ ধরনের প্রশ্নের উত্তরও এমনই: প্রসঙ্গ, সিদ্ধান্ত, দাম।
সবচেয়ে চোখে পড়া ভুল: সংজ্ঞা মুখস্থ বলে থেমে যাওয়া; প্রতিটি প্রশ্নে একটি pattern-এর নাম বসানো; “সবসময়” আর “কখনো না” দিয়ে উত্তর; আর আগের দল বা সহকর্মীকে দোষ দেওয়া। “এখানে নিশ্চিত নই, তবে এভাবে ভাবছি, আর এভাবে যাচাই করব” — এটি দুর্বলতা নয়, পরিণত বিচারের চিহ্ন।
২ · OOP আর SOLID — smell থেকে fix, LSP-র ফাঁদ আর DIP বনাম DI বনাম IoC
রিভিশন: পর্ব ২৫ · Coupling আর cohesion, পর্ব ২৬ · নকশার নীতি, পর্ব ২৭ · SOLID ১, পর্ব ২৮ · SOLID ২, পর্ব ৩১ · Interface আর contract, পর্ব ৩৭ · Dependency injection
প্র ০১শুরু · OOP-এর চারটি স্তম্ভ — encapsulation, abstraction, inheritance, polymorphism — একটিমাত্র উদাহরণে বুঝিয়ে বলুন। (সহজ)
শক্তিশালী উত্তর: একটি কাল্পনিক মোবাইল-ওয়ালেট অ্যাপের payment ধরুন। Encapsulation: Wallet-এর balance বাইরে থেকে সরাসরি বদলানো যায় না, শুধু debit(amount) দিয়ে — যেটি আগে যাচাই করে balance যথেষ্ট কি না; ফলে “balance কখনো ঋণাত্মক নয়” নিয়মটি এক জায়গায় রক্ষিত। Abstraction: checkout কোড শুধু জানে pay(amount), ভেতরে card না ওয়ালেট, তা জানে না। Inheritance: CardPayment আর WalletPayment একটি সাধারণ base থেকে আসতে পারে — তবে আজকাল প্রায়ই interface আর composition বেশি পছন্দের। Polymorphism: একই pay() ডাক, বস্তুভেদে আলাদা আচরণ — তাই নতুন পদ্ধতি এলে checkout বদলায় না।
দুর্বল উত্তর দেখতে যেমন: চারটি পাঠ্যবইয়ের সংজ্ঞা, আর উদাহরণ “Animal থেকে Dog আর Cat” — যা বাস্তব নকশার কোনো সমস্যা দেখায় না।
ফলো-আপ:
- “Encapsulation মানে কি শুধু private field আর getter-setter?” → না; প্রতিটি field-এ getter-setter দিলে encapsulation প্রায় নেই। আসল কথা invariant রক্ষা — “কী করা যায়” প্রকাশ করা, “কীভাবে রাখা আছে” লুকানো (tell, don't ask).
- “Python-এ তো private নেই?” → নিয়মটি ভাষার সুবিধার চেয়ে নকশার।
_balanceনামের প্রচলন আর শুধু method দিয়ে বদল — একই লক্ষ্য।
প্র ০২শুরু · Interface আর abstract class-এর পার্থক্য কী? কখন কোনটি বেছে নেবেন? (সহজ)
শক্তিশালী উত্তর: Interface শুধু চুক্তি — কোন method আছে; abstract class চুক্তির সাথে কিছু ভাগ করা কোড আর state-ও দিতে পারে। Java-র মতো ভাষায় একটি class একটিমাত্র class extend করতে পারে, কিন্তু অনেক interface implement করতে পারে; Java 8 থেকে interface-এ default method-ও আছে, তাই সীমারেখা আগের চেয়ে ঝাপসা। Python-এ abc.ABC বা typing.Protocol, TypeScript-এ interface — ভাষাভেদে রূপ আলাদা। সাধারণ নিয়ম: যখন শুধু “কী পারে” বলতে চান আর অনেক অসম্পর্কিত class একই ক্ষমতা পাবে — interface. যখন সত্যিকারের “is-a” পরিবার আর অনেকটা কোড ভাগ করার আছে — abstract class, তবে fragile base class-এর ঝুঁকি মাথায় রেখে।
ফলো-আপ:
- “Interface কে বানাবে — যে ব্যবহার করে, না যে বাস্তবায়ন করে?” → সাধারণত client-এর প্রয়োজন থেকে; এটিই ISP আর DIP-এর মূল কথা (প্র ০৭, ০৮)।
প্র ০৩শুরু · SRP মানে কি “একটি class একটিই কাজ করবে”? একটি smell আর তার fix দেখান। (সহজ)
শক্তিশালী উত্তর: পুরোপুরি নয়। Robert C. Martin-এর পরের ব্যাখ্যায় SRP মানে একটি module-এর দায় থাকবে একটি actor-এর কাছে — অর্থাৎ বদলের একটি উৎস। Smell: SalaryReport class বেতন হিসাব করে (হিসাব বিভাগের নিয়ম), PDF বানায় (ব্যবস্থাপনার পছন্দের চেহারা) আর email পাঠায় (IT-র SMTP সেটিং)। হিসাব বিভাগ কর-এর নিয়ম বদলাতে বললে PDF-এর কোডও খুলতে হয়, আর ভুলে email-এর test ভাঙে। Fix: SalaryCalculator, ReportRenderer, ReportMailer — প্রতিটি এক দলের বদলের কারণ। উল্টো দিকও বলুন: যে জিনিসগুলো সবসময় একসাথে বদলায়, তাদের ছড়িয়ে দেওয়াও রোগ — এক method-এর দশটি class SRP নয়।
দুর্বল উত্তর দেখতে যেমন: “প্রতিটি class-এ একটি method” — এতে cohesion ভাঙে, ফাইলের ভিড় বাড়ে।
ফলো-আপ:
- “কীভাবে টের পাবেন SRP ভাঙছে?” → class-এর নাম দিতে “আর” লাগে; বিভিন্ন কারণে বারবার একই ফাইল বদলায় (divergent change); Git history-তে ভিন্ন ভিন্ন দলের commit একই ফাইলে।
প্র ০৪মাঝারি · Open-closed principle — “বাড়ানো যায়, বদলাতে হয় না” — কোডে কেমন দেখায়? নিচের ছোট উদাহরণে smell আর fix চালিয়ে দেখান। (মাঝারি)
শক্তিশালী উত্তর: OCP-এর সতর্কসংকেত: type-এর ওপর একই switch বা if-এর শিকল কোডের কয়েক জায়গায়। নতুন ধরন এলেই প্রতিটি জায়গা খুলে বদলাতে হয়, আর একটি ভুলে গেলে bug. Fix: বদলযোগ্য অংশকে একটি ছোট চুক্তির পেছনে রাখা — polymorphism, strategy-র registry বা function-এর map — যাতে নতুন ধরন মানে নতুন কোড যোগ, পুরোনো কোড বদল নয়। নিচের block-এ একটি কাল্পনিক দোকানের ছাড়ের নিয়ম: আগে একটি switch, পরে একটি registry; একই চারটি ইনপুটে দুটির ফল মেলানো হয়েছে, তারপর নতুন ধরন “teacher” যোগ হয়েছে কোনো পুরোনো লাইন না ছুঁয়ে।
দুর্বল উত্তর দেখতে যেমন: “কোড কখনো বদলাবেন না, শুধু subclass বানাবেন।” — Bug ঠিক করতেও তো কোড বদলাতে হয়; OCP নির্দিষ্ট একটি বদলের দিক নিয়ে কথা বলে।
ফলো-আপ:
- “সব দিকে খোলা রাখবেন?” → অসম্ভব আর দামি। কোন দিকে বদল আসে তা history আর requirement দেখে অনুমান; প্রথম বদল আসার আগে abstraction প্রায়ই YAGNI-র লঙ্ঘন।
// BEFORE: one function knows every customer type; each new type means editing this switch.
function discountBefore(type, amount) {
switch (type) {
case "regular": return 0;
case "student": return Math.round(amount * 0.10);
case "corporate": return amount >= 5000 ? Math.round(amount * 0.15) : Math.round(amount * 0.05);
default: throw new Error("unknown type: " + type);
}
}
// AFTER: each rule is a small object in a registry; a new type is a new entry, the caller is untouched.
const rules = new Map([
["regular", { discount: () => 0 }],
["student", { discount: (a) => Math.round(a * 0.10) }],
["corporate", { discount: (a) => Math.round(a * (a >= 5000 ? 0.15 : 0.05)) }],
]);
function discountAfter(type, amount) {
const rule = rules.get(type);
if (!rule) throw new Error("unknown type: " + type);
return rule.discount(amount);
}
const cases = [["regular", 1200], ["student", 1200], ["corporate", 4999], ["corporate", 5000]];
let same = true;
for (const [t, a] of cases) {
const b = discountBefore(t, a), f = discountAfter(t, a);
if (b !== f) same = false;
console.log(t.padEnd(10) + String(a).padStart(5) + " before " + String(b).padStart(4) + " after " + String(f).padStart(4));
}
rules.set("teacher", { discount: (a) => Math.round(a * 0.08) }); // extension: no existing line edited
console.log("identical: " + same + " | new type teacher 1200 -> " + discountAfter("teacher", 1200));আউটপুট:
regular 1200 before 0 after 0
student 1200 before 120 after 120
corporate 4999 before 250 after 250
corporate 5000 before 750 after 750
identical: true | new type teacher 1200 -> 96
চারটি ইনপুটে আগে আর পরের ফল হুবহু এক — তাই এটি refactoring, আচরণ বদল নয়। Boundary-টি ইচ্ছা করে রাখা: ৪,৯৯৯ টাকায় ৫% (২৪৯.৯৫ → ২৫০), ৫,০০০ টাকায় ১৫% (৭৫০)। শেষ লাইনে নতুন ধরন একটি entry হিসেবে যোগ হলো, discountAfter একটুও বদলায়নি — এটিই OCP-র দৃশ্যমান লাভ। দামও বলুন: একটি switch পড়তে সহজ; মাত্র দুটি ধরন আর কোনো নতুন ধরনের সম্ভাবনা না থাকলে registry বাড়তি জটিলতা।
প্র ০৫মাঝারি · Liskov substitution principle ব্যাখ্যা করুন। Square কি Rectangle-এর subclass হতে পারে? (মাঝারি)
শক্তিশালী উত্তর: LSP: base type-এর জন্য লেখা কোডে যেকোনো subtype বসালে আচরণের প্রতিশ্রুতি ভাঙবে না। নীতিটি Barbara Liskov-এর ১৯৮৭ সালের একটি বক্তৃতা থেকে নাম পেয়েছে; Liskov ও Wing ১৯৯৪ সালে একে আনুষ্ঠানিক রূপ দেন (behavioural subtyping). গণিতে বর্গ একটি আয়তক্ষেত্র, কিন্তু setter-ওয়ালা Rectangle-এর একটি অলিখিত প্রতিশ্রুতি আছে: “width বদলালে height বদলায় না”। Square নিজের নিয়ম (চার বাহু সমান) রাখতে গেলে এই প্রতিশ্রুতি ভাঙে। নিচের block-এ একই client function — width ৫, height ৪ বসিয়ে area চায় — Rectangle-এ ২০ পায়, Square-এ ১৬. উত্তর: setter-ওয়ালা mutable রূপে না; immutable রূপে, বা দুটিকে আলাদা Shape হিসেবে রাখলে সমস্যা থাকে না।
দুর্বল উত্তর দেখতে যেমন: “হ্যাঁ, কারণ square is-a rectangle.” — “is-a” কথাটি আচরণের চুক্তি দিয়ে যাচাই না করা।
ফলো-আপ: নিচের stepper-এ পাঁচ ধাপে গভীরে।
class Rectangle {
constructor(w, h) { this.w = w; this.h = h; }
setWidth(w) { this.w = w; }
setHeight(h) { this.h = h; }
area() { return this.w * this.h; }
}
class Square extends Rectangle {
setWidth(w) { this.w = w; this.h = w; } // keeps the square's own invariant ...
setHeight(h) { this.w = h; this.h = h; } // ... but breaks Rectangle's promise
}
// Client written against Rectangle's contract: "setting the width leaves the height alone".
function stretch(r) { r.setWidth(5); r.setHeight(4); return r.area(); }
for (const shape of [new Rectangle(2, 3), new Square(2, 2)])
console.log(shape.constructor.name.padEnd(10) + "expected 20, got " + stretch(shape));
// One fix: no setters and no "is-a" between them. Each shape is immutable;
// a change returns a NEW value, so no caller's assumption can be broken.
const rect = (w, h) => Object.freeze({ kind: "rect", area: () => w * h, withWidth: (x) => rect(x, h) });
const square = (s) => Object.freeze({ kind: "square", area: () => s * s, withSide: (x) => square(x) });
const r = rect(2, 3), r2 = r.withWidth(5);
console.log("immutable: r=" + r.area() + " r2=" + r2.area() + " square(4)=" + square(4).area());আউটপুট:
Rectangle expected 20, got 20
Square expected 20, got 16
immutable: r=6 r2=15 square(4)=16
Square-এর setHeight(4) width-কেও ৪ করে দিল, তাই ২০-এর বদলে ১৬. Square নিজে “ঠিক” আচরণ করছে — ভুলটি hierarchy-তে: Rectangle-এর জন্য লেখা client-এর ধারণা ভেঙেছে। শেষ লাইনে একটি সমাধান: setter নেই, প্রতিটি বদল নতুন একটি মান ফেরত দেয় (r এখনো ৬, নতুন r2 ১৫), তাই কোনো client-এর ধারণা ভাঙার সুযোগ নেই। ইন্টারভিউতে এই “ভুলটা কোথায়” বাক্যটিই সবচেয়ে মূল্যবান।
- সংজ্ঞাবদলি বসালে ভাঙবে না
- Squareলুকানো প্রতিশ্রুতি
- Contractpre · post · invariant
- চেনাisinstance · NotImplemented
- সারানোনকশা আর test
| ইন্টারভিউয়ারের প্রশ্ন | |
| উত্তরের মূল | |
| দুর্বল উত্তরের ফাঁদ |
- সংজ্ঞা. এক বাক্যে: base type-এর জন্য লেখা যেকোনো কোড subtype পেলেও ঠিকমতো চলবে। “বসানো যায়” মানে শুধু compile হওয়া নয় — আচরণের প্রতিশ্রুতি রক্ষা। এখানে থেমে গেলে উত্তরটি মুখস্থ শোনায়; পরের প্রশ্নের জন্য নিজেই একটি উদাহরণ দিন।
- Square. ওপরের block-এর উদাহরণ: একই client function Rectangle-এ ২০ আর Square-এ ১৬ পায়। ভুল Square-এর কোডে নয়, hierarchy-তে: setter-ওয়ালা Rectangle-এর “width বদলালে height বদলায় না” প্রতিশ্রুতি Square রাখতে পারে না। এই বাক্যটি বললেই ইন্টারভিউয়ার বোঝেন আপনি “is-a”-র ফাঁদ চেনেন।
- Contract. পর্ব ২৮ আর ৩১-এর ভাষায় চারটি নিয়ম: subtype precondition কড়া করতে পারবে না (base যা মানে, subtype-ও মানবে), postcondition দুর্বল করবে না, invariant রাখবে, আর base-এ সম্ভব নয় এমন অবস্থা তৈরি করবে না — Liskov ও Wing-এর history constraint. সাথে: base যে ধরনের exception ঘোষণা করে না, তা নতুন করে ছোড়া যাবে না।
- চেনা. লক্ষণগুলো খুঁজুন: client কোডে
isinstanceবা type দেখে আলাদা পথ; override-এNotImplementedError, খালি body বা “এখানে কাজ করে না” comment; বা base-এর test subtype-এ চালালে fail. কাঠামো দেখে নয়, আচরণ দেখে — তাই সবচেয়ে ভরসার হাতিয়ার test. - সারানো. তিনটি পথ: hierarchy ভেঙে দুটি আলাদা type (দুটিই
Shape); mutable setter-এর বদলে immutable মান; বা fat base ভেঙে ছোট role interface, যাতে কেউ এমন প্রতিশ্রুতি না দেয় যা রাখতে পারবে না (ISP). আবার যেন না হয়: base-এর জন্য লেখা একটি contract test-এর সেট প্রতিটি subtype-এ চালান। এই ধাপে পৌঁছালে উত্তরটি অভিজ্ঞ-স্তরের।
প্র ০৬অভিজ্ঞ · LSP-র কয়েকটি কম পরিচিত ফাঁদ বলুন — যেখানে কোড দেখতে নির্দোষ, অথচ লঙ্ঘন। (কঠিন)
শক্তিশালী উত্তর: (১) Precondition কড়া করা: base Account.withdraw(amount) যেকোনো ধনাত্মক পরিমাণ নেয়; StudentAccount ৫০০ টাকার বেশি নিলে exception দেয় — base-এর জন্য লেখা কোড অপ্রত্যাশিতভাবে ভাঙে। নিয়মটি আসলে base-এর চুক্তিতে থাকা উচিত (“সীমা থাকতে পারে, limit() দেখে নিন”)। (২) Postcondition দুর্বল করা: base-এর sort() stable, subtype-এর নয়। (৩) Read-only subtype: ReadOnlyList extends List, আর add()-এ exception — Java-র Collections. এই নকশারই পরিচিত উদাহরণ, যেখানে চুক্তিতেই “optional operation” লিখে রাখা হয়েছে। (৪) Side effect বদল: base-এর save() সাথে সাথে লেখে, subtype buffer করে পরে লেখে — crash-এ ডেটা হারায়। (৫) সময় বা performance: base O(1)-এর প্রতিশ্রুতি দিলে subtype-এর O(n) — আচরণ এক, কিন্তু চুক্তি ভাঙা।
ফলো-আপ:
- “Exception ছুড়লে তো ঠিকই আছে, caller ধরবে?” → base যে exception ঘোষণা করেনি, caller তা ধরার কোনো কারণ জানেন না; এটিই নতুন ধরনের exception-এর নিষেধ।
- “Duck typing-এর ভাষায় LSP খাটে?” → আরও বেশি; compiler সাহায্য করে না, তাই চুক্তি থাকে docstring আর test-এ।
প্র ০৭মাঝারি · Interface segregation — একটি fat interface-এর smell আর fix দেখান। (মাঝারি)
শক্তিশালী উত্তর: Smell: একটি কাল্পনিক অনলাইন বাজার অ্যাপে PaymentProvider-এর method: charge, refund, payout, createSubscription, exportTaxReport. Cash-on-delivery-র adapter-এ এর অর্ধেক অর্থহীন, তাই throw new Error("not supported") — যা আবার LSP-রও লঙ্ঘন। Checkout শুধু charge চায়, অথচ subscription-এর method বদলালে তারও recompile আর test double বদলাতে হয়। Fix: client-এর প্রয়োজন ধরে ছোট role interface — Chargeable, Refundable, PayoutCapable. যে provider যা পারে, সেটুকুই implement করে; checkout শুধু Chargeable-এর ওপর নির্ভর করে, test-এ এক-method-এর ছোট নকলই যথেষ্ট।
দুর্বল উত্তর দেখতে যেমন: “প্রতিটি method-এর জন্য আলাদা interface.” — ভাগ হবে client-এর ব্যবহার ধরে, method গুনে নয়।
প্র ০৮মাঝারি · Dependency inversion principle-এ আসলে কী “উল্টে” যায়? একটি ছবিতে দেখান। (মাঝারি)
শক্তিশালী উত্তর: উল্টে যায় source code-এর নির্ভরতার তীর। সাধারণ নকশায় উঁচু স্তরের policy (OrderService — অর্ডারের নিয়ম) সরাসরি নিচু স্তরের detail-কে (MySQLRepository) import করে, তাই database বদলালে বা test-এ database না চাইলে policy-র কোড খুলতে হয়। DIP-এ দুজনেই একটি abstraction-এর ওপর নির্ভর করে — OrderRepository interface — আর সেই interface-এর মালিক policy-র দিক (orders package). এখন MySQL-এর কোড policy-র package-এর দিকে তাকায়, উল্টোটা নয়। লাভ: database ছাড়াই নিয়মের test (একটি in-memory fake দিয়ে), আর detail বদলালে policy অক্ষত।
DIP-এর আগে আর পরে — নির্ভরতার তীরের দিক
UML: dashed open arrow = dependency, dashed hollow triangle = realization (implements)
ফলো-আপ:
- “Interface-টি MySQL-এর package-এ রাখলে?” → তখন policy আবার detail-এর package import করে; তীর উল্টায়নি। মালিকানাই আসল কথা।
প্র ০৯অভিজ্ঞ · Dependency inversion, dependency injection আর inversion of control — তিনটি কি একই জিনিস? (কঠিন)
শক্তিশালী উত্তর: না, তিনটি ভিন্ন স্তরের কথা। DIP একটি নকশার নীতি — নির্ভরতার তীর কোন দিকে, abstraction-এর মালিক কে। DI একটি কৌশল — object তার dependency নিজে না বানিয়ে বাইরে থেকে পায়, সাধারণত constructor দিয়ে। IoC একটি বড় ধারণা — প্রোগ্রামের প্রবাহ কে চালায়: framework আপনার handler ডাকে (Hollywood principle — “আমাদের ডাকবেন না, আমরাই ডাকব”)। একটি ছাড়া অন্যটি সম্ভব: concrete MySQLRepository constructor-এ দিলে DI আছে, DIP নেই; আবার factory বা plugin registry দিয়ে DIP মানা যায় DI ছাড়াই। DI-কে IoC-এর একটি রূপ বলা যায় — dependency জোগাড়ের নিয়ন্ত্রণ বাইরে গেল। “Dependency injection” নামটি Martin Fowler-এর ২০০৪ সালের একটি প্রবন্ধ থেকে প্রচলিত।
দুর্বল উত্তর দেখতে যেমন: “Spring ব্যবহার করলেই DIP মানা হয়।” — container DI সহজ করে; তীরের দিক ঠিক করে আপনার package-এর নকশা।
ফলো-আপ:
- “সব জোড়া কোথায় হয়?” → composition root-এ — program-এর শুরুতে, যেমন
main(); বাকি কোড শুধু যা পায় তা ব্যবহার করে। - “Service locator কি DI?” → না; locator নির্ভরতা আবার লুকায় — constructor দেখে আর বোঝা যায় না class কী চায় (পর্ব ৩৭)।
প্র ১০অভিজ্ঞ · SOLID কখন বাড়াবাড়ি? একটি উদাহরণ দিন যেখানে আপনি ইচ্ছা করে একটি নীতি মানবেন না। (কঠিন)
শক্তিশালী উত্তর: SOLID-এর লক্ষ্য বদল সস্তা করা; যেখানে বদল আসবে না, সেখানে abstraction-এর দাম — বেশি file, বেশি লাফালাফি, নতুনদের বুঝতে দেরি — লাভের চেয়ে বেশি। চেনা দৃশ্য: প্রতিটি class-এর জন্য একটি interface, IPriceCalculator আর তার একমাত্র PriceCalculatorImpl. আমি তিন প্রশ্ন করি: এটি কি boundary (database, network, ঘড়ি, তৃতীয় পক্ষের API)? দ্বিতীয় বাস্তবায়ন আছে বা আসবে (test-এর fake-ও একটি)? এটি কি স্থির (ভাষার Decimal, নিজের value object)? দুই দিনের prototype বা একবারের data-migration script-এ সরাসরি sqlite3 ডাকা যুক্তিসংগত। তবে policy আর I/O আলাদা function-এ রাখার অভ্যাস প্রায় বিনামূল্যে — পরে abstraction বের করা সস্তা থাকে।
দুর্বল উত্তর দেখতে যেমন: “SOLID সবসময় মানতে হয়, নইলে কোড খারাপ।” — নীতিকে আইন বানানো; অভিজ্ঞ ইন্টারভিউয়ার ঠিক এটিই পরীক্ষা করেন।
ফলো-আপ:
- “দলে কেউ সব জায়গায় interface চান — কীভাবে বোঝাবেন?” → একটি নির্দিষ্ট class-এ দাম দেখান (file, indirection), প্রস্তাব দিন: boundary-তে interface, ভেতরে concrete, দ্বিতীয় বাস্তবায়ন এলে extract — rule of three-এর মতো।
৩ · Design pattern — কখন, কখন নয়, কোডে চেনা আর কাছাকাছি pattern-এর তফাত
রিভিশন: পর্ব ৩২ · Design pattern কী, পর্ব ৩৩ · Creational, পর্ব ৩৪ · Structural, পর্ব ৩৫ · Behavioral ১, পর্ব ৩৬ · Behavioral ২, পর্ব ৩৭ · DI আর composition
প্র ১১শুরু · Design pattern কী, আর কেন এগুলোর নাম জানা দরকার? কখন pattern ব্যবহার করবেন না? (সহজ)
শক্তিশালী উত্তর: Design pattern হলো একটি নির্দিষ্ট প্রেক্ষাপটে বারবার ফিরে আসা নকশার সমস্যার পরীক্ষিত সমাধানের ছাঁচ — কপি করার কোড নয়, মানিয়ে নেওয়ার নকশা। ধারণাটি স্থপতি Christopher Alexander-এর ১৯৭৭ সালের বই থেকে এসেছে; Gamma, Helm, Johnson ও Vlissides — “Gang of Four” — ১৯৯৪ সালের বইয়ে ২৩টি pattern তিন দলে সাজান: creational, structural, behavioral. নামের বড় লাভ যোগাযোগে: “এখানে একটি decorator দিয়ে retry মুড়ে দিন” — এক বাক্যে পুরো নকশা। কখন নয়: সমস্যা প্রমাণিত হওয়ার আগে; যেখানে ভাষার একটি function বা dictionary-ই কাজটি করে; আর যখন দলের কেউ pattern-টি চেনেন না বলে পড়া কঠিন হয়।
ফলো-আপ:
- “Pattern আর framework-এর পার্থক্য?” → pattern একটি ধারণা, framework চলমান কোড — framework-এর ভেতরে অনেক pattern থাকে।
প্র ১২মাঝারি · Singleton-এর বিরুদ্ধে সমালোচনা কী? তাহলে একটিমাত্র config বা connection pool কীভাবে রাখবেন? (মাঝারি)
শক্তিশালী উত্তর: Singleton দুটি জিনিস একসাথে দেয় — একটিমাত্র instance, আর তাতে পৌঁছানোর বৈশ্বিক পথ। প্রথমটি প্রায়ই দরকার; দ্বিতীয়টিই সমস্যা। (১) লুকানো নির্ভরতা: Config.getInstance() যেকোনো জায়গা থেকে ডাকা যায়, তাই function-এর signature দেখে বোঝা যায় না সে কী চায়। (২) Global state: এক test-এ বদলানো মান পরের test-এ থেকে যায় — ক্রমনির্ভর, flaky test. (৩) Multi-thread-এ lazy তৈরির race (thread safety). (৪) SRP: class নিজের কাজের সাথে নিজের আয়ুও সামলায়। বিকল্প: composition root-এ একবার বানিয়ে constructor দিয়ে সবাইকে দেওয়া — “একটিমাত্র” থাকে lifetime-এর সিদ্ধান্ত হিসেবে, global পথ ছাড়া। Python বা JavaScript-এ module-এর ভেতরের একটি instance-ও প্রায়ই যথেষ্ট, তবে test-এ বদলানোর সুযোগ রেখে।
দুর্বল উত্তর দেখতে যেমন: “Singleton একটি anti-pattern, কখনো নয়।” — logger বা hardware driver-এর মতো ক্ষেত্রে একটিমাত্র instance যুক্তিসংগত; সমস্যা global পথে।
ফলো-আপ:
- “DI container-এর ‘singleton lifetime’ কি একই জিনিস?” → না; container একটি instance সবাইকে দেয়, কিন্তু class নিজে জানে না, আর test-এ নতুন instance বানানো যায়।
প্র ১৩মাঝারি · Strategy আর State-এর class diagram প্রায় একই। তাহলে পার্থক্য কোথায়? (মাঝারি)
শক্তিশালী উত্তর: গঠন এক — একটি context, একটি interface, কয়েকটি বাস্তবায়ন। তফাত হলো কে বদলায় আর কেন। Strategy-তে বাইরের client একটি পদ্ধতি বেছে দেন — যেমন delivery-র খরচ হিসাবের তিনটি নিয়ম (সাধারণ, express, পিকআপ পয়েন্ট); একবার বাছলে context নিজে থেকে বদলায় না, আর strategy-রা একে অপরকে চেনে না। State-এ object নিজের অবস্থা বদলায়, ঘটনার জবাবে — একটি অর্ডার Placed → Paid → Shipped → Delivered; Paid অবস্থায় cancel() refund শুরু করে, Shipped-এ বাতিল নিষেধ। এখানে অবস্থাগুলো প্রায়ই পরের অবস্থা জানে। এক বাক্যে: Strategy = “কীভাবে করব”, বাইরে থেকে বাছা; State = “এখন আমি কী”, ভেতর থেকে বদলায়।
দুর্বল উত্তর দেখতে যেমন: “দুটোই একই, শুধু নাম আলাদা।” — Intent না বলা।
ফলো-আপ:
- “State pattern না একটি transition table?” → অবস্থা কম আর আচরণ সরল হলে table বা enum-এর map বেশি পরিষ্কার, আর পর্ব ৩০-এর state machine diagram-এর সাথে সরাসরি মেলে; অবস্থাভেদে আচরণ জটিল হলে class.
- “Template method আর Strategy?” → template method inheritance দিয়ে কাঠামো স্থির রেখে ধাপ বদলায়; strategy composition দিয়ে পুরো পদ্ধতি বদলায় — runtime-এ বদলানো যায়, test-এ সহজ।
প্র ১৪মাঝারি · Adapter, Facade, Decorator আর Proxy — চারটিই একটি মোড়ক (wrapper). একটি টেবিলে পার্থক্য বলুন। (মাঝারি)
শক্তিশালী উত্তর: প্রশ্ন দুটি: মোড়কের পরে interface বদলায় কি না, আর উদ্দেশ্য কী।
| Pattern | Interface | উদ্দেশ্য | একটি উদাহরণ |
|---|---|---|---|
| Adapter | বদলায় — অমিল একটি API-কে আপনার চুক্তিতে অনুবাদ | মিলিয়ে দেওয়া | বাইরের SDK-র makeTxn(paisa)-কে আপনার pay(taka) |
| Facade | নতুন, সরল — অনেকগুলোর সামনে একটি দরজা | জটিলতা লুকানো | placeOrder() ভেতরে stock, payment, invoice, SMS ডাকে |
| Decorator | একই | আচরণ যোগ করা; মোড়ক একের পর এক | withRetry(withLogging(client)) — ক্রম বদলালে ফল বদলায় |
| Proxy | একই | আসলটির কাছে যাওয়া নিয়ন্ত্রণ | দেরিতে তৈরি, অনুমতি যাচাই, দূরের ডাক, cache |
Decorator আর Proxy-র কোড প্রায় একই দেখাতে পারে; তফাত intent-এ। Decorator সাধারণত client বাইরে থেকে জোড়েন, প্রয়োজনমতো অনেক স্তরে; proxy সাধারণত আসলটির জায়গায় চুপচাপ দাঁড়ায়, আর আসলটির আয়ু বা প্রবেশাধিকার সামলায়।
ফলো-আপ:
- “Facade কি god class হয়ে যেতে পারে?” → হ্যাঁ, সব কাজ নিজেই করতে শুরু করলে। Facade শুধু সমন্বয় করবে, নিয়ম রাখবে নিচের অংশগুলো।
প্র ১৫মাঝারি · কোডে pattern চিনুন: তিনটি ছোট টুকরো দেখে বলুন কোনটি কোন pattern, আর কেন। (মাঝারি)
টুকরোগুলো (JavaScript, শুধু দেখার জন্য):
// A
const send = withRetry(3, withTimeout(2000, smsClient.send));
// B
app.use(auth); app.use(rateLimit); app.use(validate); app.use(handler);
// C
history.push({ do: () => cart.add(item), undo: () => cart.remove(item) });
শক্তিশালী উত্তর: A — Decorator: প্রতিটি মোড়ক একই চুক্তি (একটি send function) রেখে আচরণ যোগ করে; ক্রম গুরুত্বপূর্ণ — এখানে প্রতিটি চেষ্টার নিজস্ব timeout, উল্টো ক্রমে তিনবার চেষ্টার মোট সময়ে একটি timeout. B — Chain of responsibility: অনুরোধ handler থেকে handler-এ যায়; যেকোনো একজন থামিয়ে দিতে পারে (auth ব্যর্থ হলে 401). শিকলের শেষে একটি default handler রাখা জরুরি। C — Command: একটি কাজকে object বানানো হয়েছে, সাথে তার উল্টো কাজ — তাই রাখা, সারিতে দেওয়া আর undo/redo সম্ভব। লক্ষ করুন, তিনটির একটিতেও class নেই — function-ই যথেষ্ট; pattern চেনা যায় intent দেখে, class diagram দেখে নয়।
ফলো-আপ:
- “B কি Pipes and filters নয়?” → কাছাকাছি; pipe-filter-এ প্রতিটি ধাপ ডেটা বদলে পরের ধাপে দেয় আর সবাই চলে, chain-এ যেকোনো handler থামাতে পারে। অনেক middleware দুটির মিশ্রণ।
প্র ১৬অভিজ্ঞ · একজন সহকর্মী প্রতিটি নতুন feature-এ তিন-চারটি pattern বসান। Review-এ কী বলবেন? (কঠিন)
শক্তিশালী উত্তর: প্রথমে বুঝতে চাইব কোন বদলের আশঙ্কায় তিনি এগুলো বসিয়েছেন — হয়তো তিনি এমন কিছু জানেন যা আমি জানি না। তারপর নির্দিষ্টভাবে দাম দেখাব: “এই Notification-এর একটিমাত্র বাস্তবায়ন আছে; একটি function দিয়ে ১২ লাইনে একই কাজ হয়, আর নতুন কেউ পড়লে চারটি file ঘুরতে হবে না। দ্বিতীয় channel এলে তখন strategy বের করা ২০ মিনিটের refactor.” এটি golden hammer-এর লক্ষণ — হাতে হাতুড়ি থাকলে সবকিছু পেরেক। মন্তব্যটি আদেশ নয়, প্রশ্ন হিসেবে, আর merge আটকানোর মতো না হলে “non-blocking” লেবেলসহ। দলের স্তরে: একটি ছোট নির্দেশিকা — “pattern আসবে দ্বিতীয় বা তৃতীয় প্রয়োজনে”।
দুর্বল উত্তর দেখতে যেমন: “Over-engineering, সরিয়ে ফেলুন।” — কারণ, বিকল্প আর সম্মান ছাড়া।
৪ · Clean code, refactoring আর code review — একটি snippet review করা, smell থেকে refactoring
রিভিশন: পর্ব ৪১ · Clean code, পর্ব ৪২ · Code smell আর refactoring, পর্ব ৪৩ · Technical debt আর legacy, পর্ব ৪৪ · Code review, পর্ব ১৬ · Pull request
অনেক ইন্টারভিউতে একটি ছোট কোড দেখিয়ে বলা হয়, “এটি একটি PR — review করুন।” এখানে দেখা হয় আপনি কী আগে দেখেন, কীভাবে বলেন, আর আচরণ না বদলে কোড ভালো করতে পারেন কি না। নিচের snippet একটি কাল্পনিক মুদি-দোকানের অনলাইন অর্ডারের বিল হিসাব করে: d অর্ডারের তালিকা, m বাড়িতে delivery কি না, f ক্রেতা সদস্য কি না।
def calc(d, m, f):
t = 0
for x in d:
if x["status"] == "paid":
if f:
t = t + x["amount"] * 0.85
else:
t = t + x["amount"]
if m:
t = t + 60
return tপ্র ১৭মাঝারি · ওপরের calc(d, m, f) একটি PR-এ এসেছে। নমুনা review মন্তব্য লিখুন — কোনটি merge আটকাবে, কোনটি নয়। (মাঝারি)
শক্তিশালী উত্তর: আগে বড় প্রশ্ন (সঠিকতা আর test), তারপর পড়ার সুবিধা, শেষে রুচি। প্রতিটি মন্তব্য নির্দিষ্ট, কারণসহ, আর লেবেল দেওয়া (Conventional Comments-এর ধাঁচে):
- question (blocking): “এই function-এর কোনো test দেখছি না। সদস্য আর delivery-র চারটি মিশ্রণ, আর cancelled অর্ডার — এগুলোর test যোগ করবেন? Refactor বা পরের বদলের আগে এটিই নিরাপত্তার জাল।”
- issue (blocking): “টাকা
float-এ হিসাব হচ্ছে (* 0.85) — যোগফলে পয়সার ভগ্নাংশ জমতে পারে। বিল কি পয়সায় পূর্ণসংখ্যা, বাDecimal-এ রাখার নিয়ম আছে? থাকলে সেটি আলাদা PR-এ, test-সহ, কারণ এতে আচরণ বদলাবে।” - suggestion: “
calc,d,m,fনাম থেকে কিছু বোঝা যায় না; ডাকার জায়গায়calc(items, True, False)দেখে কোনটি কী, জানার উপায় নেই।bill_total(orders, is_member=..., home_delivery=...)— keyword-only flag হলে ডাকার জায়গা নিজেই বোঝায়।” - suggestion: “
0.85আর60magic number — নামওয়ালা constant করলে, যেমনHOME_DELIVERY_FEE = 60, পরের বার ফি বদলাতে খুঁজতে হবে না।” - nit (non-blocking): “ভেতরের if-else দুই জায়গায় একই যোগ লেখে; একটি rate ঠিক করে একবার যোগ করলে পড়া সহজ।”
- praise: “cancelled অর্ডার বাদ দেওয়ার শর্তটি পরিষ্কার — ভালো।”
দুর্বল উত্তর দেখতে যেমন: “Clean code নয়, আবার লিখুন।” — অস্পষ্ট, কারণ ছাড়া; বা শুধু space আর quote-এর মতো রুচির মন্তব্য, যা formatter-এর কাজ, আর float-এর ঝুঁকি চোখ এড়িয়ে যাওয়া।
ফলো-আপ:
- “এখন নিজে refactor করে দেখান।” → নিচের block: আগের আর পরের function একই ১২টি মিশ্রণে চালিয়ে মেলানো। Float-এর প্রশ্নটি ইচ্ছা করে এই refactor-এ ছোঁয়া হয়নি — সেটি আচরণ বদল।
from itertools import product
def calc(d, m, f): # BEFORE (the code under review)
t = 0
for x in d:
if x["status"] == "paid":
if f:
t = t + x["amount"] * 0.85
else:
t = t + x["amount"]
if m:
t = t + 60
return t
MEMBER_PRICE_RATE = 0.85 # AFTER: members pay 85% of the price
HOME_DELIVERY_FEE = 60 # taka, added once per bill
def paid_subtotal(orders, is_member):
rate = MEMBER_PRICE_RATE if is_member else 1
return sum(o["amount"] * rate for o in orders if o["status"] == "paid")
def bill_total(orders, *, is_member, home_delivery):
fee = HOME_DELIVERY_FEE if home_delivery else 0
return paid_subtotal(orders, is_member) + fee
baskets = [[], [{"status": "paid", "amount": 400}, {"status": "cancelled", "amount": 900}],
[{"status": "paid", "amount": 120}, {"status": "paid", "amount": 75}]]
same = True
for b, (home, member) in product(baskets, product([False, True], repeat=2)):
old, new = calc(b, home, member), bill_total(b, is_member=member, home_delivery=home)
same = same and old == new
print(f"items={len(b)} home={home!s:5} member={member!s:5} before={old!s:6} after={new}")
print("identical for all", len(baskets) * 4, "cases:", same)আউটপুট:
items=0 home=False member=False before=0 after=0
items=0 home=False member=True before=0 after=0
items=0 home=True member=False before=60 after=60
items=0 home=True member=True before=60 after=60
items=2 home=False member=False before=400 after=400
items=2 home=False member=True before=340.0 after=340.0
items=2 home=True member=False before=460 after=460
items=2 home=True member=True before=400.0 after=400.0
items=2 home=False member=False before=195 after=195
items=2 home=False member=True before=165.75 after=165.75
items=2 home=True member=False before=255 after=255
items=2 home=True member=True before=225.75 after=225.75
identical for all 12 cases: True
তিনটি ঝুড়ি (খালি; একটি paid আর একটি cancelled; দুটি paid) × delivery আর সদস্যপদের চারটি মিশ্রণ = ১২টি ঘটনা, প্রতিটিতে আগে আর পরের ফল হুবহু এক — এমনকি int বনাম float-এর রূপও (400 বনাম 340.0). এটিই refactoring-এর সংজ্ঞা: বাইরের আচরণ একই, ভেতরটা পরিষ্কার — নাম, constant, keyword-only flag আর একটি ছোট paid_subtotal. ৯০০ টাকার cancelled অর্ডার কোনো ঘটনাতেই যোগ হয়নি। সদস্যের ১২০ + ৭৫ টাকা = ১০২ + ৬৩.৭৫ = ১৬৫.৭৫ — এখানেই float-এর পয়সার ভগ্নাংশ, যা আলাদা PR-এর বিষয়।
প্র ১৮মাঝারি · কয়েকটি code smell আর প্রতিটির সাথে মানানসই refactoring বলুন। (মাঝারি)
শক্তিশালী উত্তর: Code smell লক্ষণ, রোগনির্ণয় নয় — দেখার পরে বিচার লাগে। Beck ও Fowler-এর তালিকা থেকে:
| Smell | লক্ষণ | Refactoring |
|---|---|---|
| Long function | স্ক্রল করে পড়তে হয়, ভেতরে comment-এর শিরোনাম | Extract function |
| Long parameter list / data clumps | একই তিনটি parameter সবসময় একসাথে | Introduce parameter object |
| একই switch বারবার | type দেখে কয়েক জায়গায় শাখা | Replace conditional with polymorphism |
| Feature envy | method অন্য class-এর ডেটা বেশি ছোঁয় | Move function |
| Shotgun surgery | একটি বদলে দশটি file | Move function/field, একত্র করা |
| Primitive obsession | টাকা, ফোন নম্বর, তারিখ সবই string বা float | Replace primitive with object (value object) |
| God class | হাজার লাইন, সবাই এটিকে চেনে | Extract class, দায় ধরে ভাগ |
ফলো-আপ:
- “Duplicate কোড দেখলেই জোড়া লাগাবেন?” → না; দেখতে এক কিন্তু আলাদা কারণে বদলায় এমন কোড (incidental duplication) জোড়া লাগালে ভুল abstraction তৈরি হয়। DRY মানে জ্ঞানের একটি উৎস।
প্র ১৯অভিজ্ঞ · Product manager বলছেন “refactoring-এর সময় নেই, feature দিন।” Technical debt-এর কথা কীভাবে বোঝাবেন? (কঠিন)
শক্তিশালী উত্তর: “Clean code” নয়, ব্যবসার ভাষায়। Technical debt রূপকটি Ward Cunningham-এর (১৯৯২): শর্টকাট হলো ধার (principal), আর প্রতিটি পরের বদলে বাড়তি সময় হলো সুদ (interest). আমি সুদ মাপি: “গত তিন sprint-এ checkout-এর প্রতিটি ছোট বদলে গড়ে দুই দিন বাড়তি গেছে, আর ওই module থেকেই production-এর চারটি bug.” Git history থেকে hotspot — যে file ঘনঘন বদলায় আর জটিল — দেখাই। তারপর ছোট, মাপা প্রস্তাব: আলাদা “refactoring sprint” নয়, বরং পরের checkout feature-এর অনুমানে ২০% সময় ওই অংশ গোছাতে, আর সাফল্যের মাপ ঠিক করা (বদলের সময়, bug-এর সংখ্যা)। যে debt-এর সুদ নেই — কেউ ছোঁয় না এমন পুরোনো কোড — তা আপাতত থাক।
দুর্বল উত্তর দেখতে যেমন: “কোড খুব খারাপ, পুরোটা আবার লিখতে হবে।” — কোনো সংখ্যা ছাড়া, আর সবচেয়ে ঝুঁকির প্রস্তাব দিয়ে।
ফলো-আপ:
- “সব debt কি খারাপ?” → না; Fowler-এর quadrant-এ জেনেশুনে, বিবেচনা করে নেওয়া debt — যেমন বাজার যাচাইয়ের জন্য দ্রুত একটি সংস্করণ — যুক্তিসংগত, যদি শোধের পরিকল্পনা থাকে।
প্র ২০অভিজ্ঞ · পুরোনো একটি system “নতুন করে লিখে ফেলি” — দলের এই প্রস্তাবে আপনি কী বলবেন? (কঠিন)
শক্তিশালী উত্তর: বড় rewrite-এর পরিচিত ঝুঁকি: পুরোনো system-এ বছরের পর বছর জমা অলিখিত নিয়ম আর bug fix থাকে, যা কেউ মনে রাখেন না; rewrite চলাকালীন পুরোনোটিতেও বদল চলতে থাকে — দুটি লক্ষ্য একসাথে তাড়া করা; আর মাসের পর মাস ব্যবহারকারী নতুন কিছু পান না। বিকল্প strangler fig: পুরোনোর সামনে একটি routing স্তর, তারপর একটি একটি অংশ নতুন কোডে সরানো, প্রতিটি ধাপ production-এ যাচাই, শেষে পুরোনোটি শুকিয়ে যায়। নতুন কাজ পুরোনো কোডে sprout বা wrap করে। Rewrite যুক্তিসংগত হতে পারে যখন system ছোট, প্রযুক্তি আর চালানো যায় না, বা নিয়মগুলো আসলেই বদলে গেছে — তখনও আগে characterization test দিয়ে আজকের আচরণ ধরে রাখা।
৫ · Testing — test-এর স্তর, mock-stub-fake, TDD, coverage-এর সীমা, flaky test আর legacy কোড
রিভিশন: পর্ব ৪৫ · Testing-এর মৌলিক, পর্ব ৪৬ · Unit test, পর্ব ৪৭ · Test case নকশা, পর্ব ৪৮ · TDD আর BDD, পর্ব ৪৯ · Test double, পর্ব ৫০ · Coverage আর complexity, পর্ব ৫১ · Test pyramid, পর্ব ৪৩ · Legacy code
প্র ২১শুরু · Unit test, integration test আর end-to-end test-এর পার্থক্য কী? কোনটি কতগুলো লিখবেন? (সহজ)
শক্তিশালী উত্তর: পার্থক্য কতটুকু জোড়া লাগিয়ে দেখছেন। Unit test: একটি function, class বা একটি আচরণ — মিলিসেকেন্ডে চলে, ব্যর্থ হলে ঠিক কোথায় ভুল বোঝা যায়। Integration test: নিজের কোড আসল database, file বা অন্য service-এর সাথে — ধীর, কিন্তু সংযোগের ভুল (ভুল SQL, ভুল field-এর নাম) ধরে। End-to-end test: ব্যবহারকারীর পুরো পথ, browser থেকে database পর্যন্ত — সবচেয়ে বেশি আস্থা, সবচেয়ে ধীর আর ভঙ্গুর। Test pyramid তাই বাজেটের নিয়ম: নিচে অনেক unit, মাঝে কম integration, ওপরে অল্প কয়েকটি end-to-end. Testing trophy বা honeycomb-এর মতো ভিন্ন আকারের প্রস্তাবও আছে; সবাই একমত যে উল্টো আকার — ice-cream cone, অনেক হাতে করা আর UI test, অল্প unit — এড়াতে হবে।
ফলো-আপ:
- “‘Unit’ কতটুকু?” → দুটি ধারা: সহযোগীদের নকল দিয়ে একা যাচাই (solitary), আর আসল ছোট সহযোগীসহ (sociable). সংজ্ঞায় মতভেদ আছে — এটি স্বীকার করাই ভালো উত্তর।
প্র ২২শুরু · একটি function বয়স নেয় আর ১৮ থেকে ৬০ বছর (দুটিই ধরে) হলে আবেদন গ্রহণ করে। কোন কোন test case লিখবেন? (সহজ)
শক্তিশালী উত্তর: সব বয়স test করা অসম্ভব আর অপ্রয়োজনীয়; ইনপুটকে ভাগ করি যেখানে আচরণ একই (equivalence partitioning): ১৮-এর কম (বাতিল), ১৮–৬০ (গ্রহণ), ৬০-এর বেশি (বাতিল) — প্রতিটি থেকে একটি, যেমন ১০, ৩৫, ৭০. তারপর ভুল সবচেয়ে বেশি হয় সীমানায় (boundary value analysis): ১৭, ১৮, ৬০, ৬১ — >= আর >-এর ভুল এখানেই ধরা পড়ে। সাথে অবৈধ ইনপুট: ঋণাত্মক, দশমিক, খালি বা লেখা — requirement কী বলে, তা জিজ্ঞেস করি (এটিও একটি clarifying question).
দুর্বল উত্তর দেখতে যেমন: “২৫ আর ৪৫ দিয়ে test করব।” — দুটিই একই ভাগে, সীমানা অদেখা।
প্র ২৩মাঝারি · Mock, stub, fake আর spy — পার্থক্য কী? ছোট কোডে দেখান। (মাঝারি)
শক্তিশালী উত্তর: সবগুলো test double — আসল সহযোগীর নিয়ন্ত্রিত নকল; শব্দগুলো Gerard Meszaros-এর ২০০৭ সালের বই থেকে প্রচলিত, আর Martin Fowler-এর “Mocks Aren't Stubs” প্রবন্ধ এগুলো জনপ্রিয় করে। Dummy শুধু জায়গা ভরে। Stub ঠিক করা উত্তর দেয় — “আজকের তারিখ ১০ মার্চ”। Spy ডাকগুলো লিখে রাখে, test পরে দেখে। Mock আগে থেকে প্রত্যাশা জানে আর নিজে যাচাই করে। Fake সত্যি কাজ করে, সরলভাবে — in-memory database. মূল পার্থক্য যাচাইয়ের ধরনে: stub ও fake দিয়ে state verification (ফল কী হলো), mock ও spy দিয়ে behaviour verification (কোন ডাক, কোন argument). বাস্তবে অনেক library সবকিছুকেই “mock” বলে — যেমন Python-এর unittest.mock — তাই নামের চেয়ে ভূমিকা বলুন। নিচের block-এ একটি কাল্পনিক পাঠাগারের দেরির reminder: ঘড়ি stub, repository fake, SMS কখনো spy, কখনো mock.
দুর্বল উত্তর দেখতে যেমন: “Mock মানে যেকোনো নকল object.” — library-র নাম আর ধারণা গুলিয়ে ফেলা।
// Code under test: remind members whose book is overdue. Repository, clock and SMS are injected.
function remindOverdue(repo, clock, sms) {
let sent = 0;
for (const loan of repo.openLoans())
if (loan.due < clock.today()) { sms.send(loan.phone, `Book "${loan.title}" is overdue`); sent++; }
return sent;
}
// stub = canned answer; spy = records calls; mock = expectations set first, checks itself;
// fake = a real but simple implementation (an in-memory list instead of a database).
const stubClock = { today: () => "2026-03-10" };
const makeSpy = () => { const calls = []; return { calls, send: (...a) => calls.push(a) }; };
const makeMock = (expected) => { const got = []; return { send: (...a) => got.push(a.join("|")),
verify: () => { if (got.join(";") !== expected.join(";")) throw new Error("mock got " + got.join(";")); } }; };
class FakeRepo { constructor(l) { this.l = l; } openLoans() { return this.l.filter((x) => !x.returned); } }
const repo = new FakeRepo([
{ title: "Padma", phone: "0100-0001", due: "2026-03-01", returned: false },
{ title: "Gitanjali", phone: "0100-0002", due: "2026-03-20", returned: false },
{ title: "Debdas", phone: "0100-0003", due: "2026-02-15", returned: true }]);
const tests = {
"state: one reminder sent (stub + fake)": () => {
if (remindOverdue(repo, stubClock, makeSpy()) !== 1) throw new Error("count"); },
"spy: message text checked after the call": () => { const spy = makeSpy(); remindOverdue(repo, stubClock, spy);
if (spy.calls[0][1] !== 'Book "Padma" is overdue') throw new Error(spy.calls[0][1]); },
"mock: expectation set before, verified after": () => {
const mock = makeMock(['0100-0001|Book "Padma" is overdue']); remindOverdue(repo, stubClock, mock); mock.verify(); },
};
let pass = 0;
for (const [name, fn] of Object.entries(tests))
try { fn(); pass++; console.log("PASS " + name); } catch (e) { console.log("FAIL " + name + " - " + e.message); }
console.log(pass + "/" + Object.keys(tests).length + " passed; no real SMS, database or clock was used");আউটপুট:
PASS state: one reminder sent (stub + fake)
PASS spy: message text checked after the call
PASS mock: expectation set before, verified after
3/3 passed; no real SMS, database or clock was used
তিনটি ধার: “Padma”-র মেয়াদ ১ মার্চ পেরিয়েছে, “Gitanjali”-র ২০ মার্চ এখনো আসেনি, “Debdas” ফেরত দেওয়া — তাই stub-এর ১০ মার্চে একটিই SMS. প্রথম test ফল দেখে (state), দ্বিতীয়টি spy-র লেখা ডাক পরে দেখে, তৃতীয়টিতে প্রত্যাশা আগে বসানো আর mock নিজেই verify() করে (behaviour). লক্ষ করুন, তিনটির কোনোটিই সত্যি SMS পাঠায়নি বা আসল ঘড়ি পড়েনি — dependency বাইরে থেকে দেওয়া হয়েছে বলেই এটি সম্ভব (প্র ০৯)।
প্র ২৪মাঝারি · কখন mock করবেন না? বেশি mock-এর ক্ষতি কী? (মাঝারি)
শক্তিশালী উত্তর: (১) Value বা ছোট pure কোড mock করবেন না — Money, তারিখের হিসাব — আসলটিই দ্রুত আর নির্ভুল। (২) নিজের নয় এমন API সরাসরি mock না করে — “don't mock what you don't own” — নিজের একটি ছোট adapter-এর পেছনে রাখুন; adapter-কে আসল (বা sandbox) API-র বিরুদ্ধে কয়েকটি integration test দিয়ে যাচাই করুন, বাকি কোডে adapter-এর নকল। (৩) বেশি mock মানে test implementation-এর সাথে বাঁধা: ভেতরের ডাকের ক্রম বদলালেই লাল, অথচ আচরণ ঠিক — এমন test refactoring আটকায়। (৪) Mock drift: আসল service বদলেছে, mock পুরোনো — সব সবুজ, production-এ ভাঙা; প্রতিকার contract test.
ফলো-আপ:
- “Classicist না mockist?” → দুই ধারারই যুক্তি আছে; আমি সাধারণত state verification পছন্দ করি, আর behaviour verification রাখি সেখানে, যেখানে ডাকটাই ফল — যেমন “SMS পাঠানো হলো কি না”।
প্র ২৫মাঝারি · TDD কী? এটি কি কাজ ধীর করে না? (মাঝারি)
শক্তিশালী উত্তর: TDD — Kent Beck-এর ছন্দ (Test-Driven Development: By Example, ২০০২): red → green → refactor. আগে একটি ছোট ব্যর্থ test (red), তারপর সবচেয়ে সরল কোড যা তাকে পাস করায় (green), তারপর test সবুজ রেখে পরিষ্কার করা (refactor) — প্রতিটি চক্র কয়েক মিনিটের। এর বড় লাভ নকশায়: আগে test লিখতে হলে কোডকে বাইরে থেকে ডাকা আর dependency বাইরে থেকে দেওয়া সহজ হতে হয়, তাই coupling কমে। ধীর কি না — সৎ উত্তর: প্রমাণ মিশ্র; কিছু শিল্প-গবেষণায় কম defect আর শুরুতে বেশি সময়ের কথা এসেছে, নিয়ন্ত্রিত পরীক্ষায় ফল অস্পষ্ট। আমি TDD বেশি কাজে লাগাই স্পষ্ট নিয়মের logic-এ (হিসাব, validation, state machine); অজানা কিছু শেখার spike বা UI-র চেহারা খোঁজায় আগে শিখি, পরে test.
দুর্বল উত্তর দেখতে যেমন: “TDD মানে কোড শেষে test লেখা।” — বা “TDD সবসময় সব জায়গায় করতে হবে।”
প্র ২৬মাঝারি · আমাদের coverage ১০০%, তবুও production-এ bug — কীভাবে সম্ভব? (মাঝারি)
শক্তিশালী উত্তর: Coverage বলে কোন কোড চলেছে, ঠিক চলেছে কি না নয়। কয়েকটি কারণ: (১) assertion ছাড়া test-ও লাইন চালায় — ১০০% coverage, শূন্য যাচাই; (২) statement coverage ১০০% হলেও branch-এর একটি দিক বা শর্তের মিশ্রণ বাদ থাকতে পারে; (৩) যে কোড লেখাই হয়নি — ভুলে যাওয়া একটি null বা timeout-এর পথ — তা coverage-এ কখনো দেখা যায় না; (৪) requirement-ই ভুল বোঝা। Coverage লক্ষ্য হলে Goodhart-এর সূত্রে নষ্ট হয় — মানুষ সংখ্যা মেলাতে অর্থহীন test লেখেন। তাই coverage দেখি “কোথায় test নেই” খুঁজতে। Test আসলে কতটা ধরে, তার ভালো মাপ mutation testing: কোডে ছোট ভুল (>= → >) ঢুকিয়ে দেখা test লাল হয় কি না; mutation score = মারা পড়া ÷ (মোট − equivalent).
প্র ২৭মাঝারি · একটি test কখনো পাস করে, কখনো ফেল — কোনো কোড না বদলেই। কী করবেন? (মাঝারি)
শক্তিশালী উত্তর: এটি flaky test, আর এটি দলের আস্থা খায় — লাল দেখে সবাই “আবার চালাও” ভাবতে শেখেন, আসল ব্যর্থতাও তখন চোখ এড়ায়। সাধারণ কারণ: আসল ঘড়ি বা সময়-অঞ্চল, নির্দিষ্ট sleep, test-এর মধ্যে ভাগ করা অবস্থা আর ক্রম, বাইরের service, seed ছাড়া এলোমেলো মান, concurrency. পদক্ষেপ: পরিসংখ্যান নিয়ে নিশ্চিত হওয়া (একই commit-এ বারবার চালানো), মালিকসহ quarantine যাতে main আটকে না থাকে, তারপর কারণ ধরে সমাধান — অন্ধ retry দিয়ে ঢাকা নয়। নিচের block-এ সবচেয়ে চেনা কারণটি: একটি “happy hour” ছাড়ের test আসল ঘড়ি পড়ত, তাই দিনের ২৪ ঘণ্টার মধ্যে মাত্র ২ ঘণ্টা পাস করত।
// Rule: a 20% "happy hour" discount applies from 15:00 to 16:59.
// BEFORE: the function reads the real clock itself, so its test depends on WHEN CI runs.
function priceBefore(amount, readClock) { const h = readClock().getHours();
return h >= 15 && h < 17 ? amount * 0.8 : amount; }
// AFTER: the caller passes the hour in (the clock is a dependency), so a test chooses the time.
function priceAfter(amount, hour) { return hour >= 15 && hour < 17 ? amount * 0.8 : amount; }
// The old test expected the discount without fixing the time. Simulate CI running it at each hour:
let passedHours = [];
for (let h = 0; h < 24; h++) {
const fakeNow = () => new Date(2026, 2, 10, h, 30); // stands in for "whenever CI happens to run"
if (priceBefore(500, fakeNow) === 400) passedHours.push(h);
}
console.log("old test passes only at hours: " + passedHours.join(", ") + " (" + passedHours.length + "/24)");
// New tests state the time explicitly: each one gives the same result at any hour of the day.
const cases = [[14, 500], [15, 400], [16, 400], [17, 500]];
const ok = cases.every(([h, want]) => priceAfter(500, h) === want);
console.log("new boundary tests at 14, 15, 16, 17 -> " + (ok ? "all pass, every run" : "FAIL"));
// Same behaviour as before for every hour (refactor check):
let same = true;
for (let h = 0; h < 24; h++) same = same && priceBefore(500, () => new Date(2026, 2, 10, h)) === priceAfter(500, h);
console.log("before and after agree for all 24 hours: " + same);আউটপুট:
old test passes only at hours: 15, 16 (2/24)
new boundary tests at 14, 15, 16, 17 -> all pass, every run
before and after agree for all 24 hours: true
পুরোনো test সময় ঠিক না করেই ছাড় আশা করত — CI যখন বিকেল ৩টা বা ৪টায় চলে, তখনই শুধু সবুজ (২৪-এর মধ্যে ২)। সমাধান ঘড়িকে dependency বানানো: function এখন ঘণ্টা বাইরে থেকে পায়, আর নতুন test-গুলো সময় নিজে বলে দেয় — সীমানায় ১৪, ১৫, ১৬, ১৭. শেষ লাইন দেখায় এটি refactoring: ২৪টি ঘণ্টার প্রতিটিতে আগে আর পরের দাম একই। এটিই প্র ০৮-এর DIP-এর সবচেয়ে দৈনন্দিন লাভ।
প্র ২৮অভিজ্ঞ · Test ছাড়া পুরোনো একটি function-এ বদল আনতে হবে। কীভাবে নিরাপদে এগোবেন? (কঠিন)
শক্তিশালী উত্তর: Michael Feathers-এর সংজ্ঞায় legacy code মানে test ছাড়া কোড (Working Effectively with Legacy Code, ২০০৪)। ধাপ: (১) বদলের জায়গা চিহ্নিত করা। (২) Characterization test: কোডটি আজ কী করে, তা test-এ ধরে রাখা — “কী করা উচিত” নয়। (৩) Test বসানোর জায়গা না থাকলে একটি seam খোঁজা — যেখানে সেই জায়গার কোড না বদলেই আচরণ বদলানো যায়, যেমন একটি parameter বা ছোট extract. (৪) তারপর ছোট ধাপে refactor আর বদল। নিচের block-এ একটি কাল্পনিক জরিমানার function: characterization করতে গিয়েই ধরা পড়ল, তিন বছরের বেশি পুরোনো সদস্য এক দিন দেরি করলে জরিমানা −৫ টাকা! নিয়ম: অবাক করা আচরণটিও আপাতত test-এ ধরে রাখা, product owner-কে জিজ্ঞেস করা, আর fix আলাদা বদলে — নইলে refactor আর bug fix মিশে যায়।
দুর্বল উত্তর দেখতে যেমন: “আগে পুরো function নতুন করে লিখে তারপর test লিখব।” — আজকের অলিখিত আচরণ হারানোর নিশ্চিত পথ।
import io, unittest
def late_fee(days_late, member_years): # legacy code: no tests, nobody remembers the rules
if days_late <= 0:
return 0
fee = days_late * 5
if member_years > 3:
fee = fee - 10
if fee > 200:
fee = 200
return fee
class CharacterizeLateFee(unittest.TestCase):
"""Pin down what the code DOES today, before changing anything."""
def test_on_time_is_free(self):
self.assertEqual(late_fee(0, 1), 0)
def test_five_taka_per_day(self):
self.assertEqual(late_fee(4, 1), 20)
def test_cap_is_200(self):
self.assertEqual(late_fee(100, 1), 200)
def test_old_member_one_day_late(self):
# Surprise found while characterizing: the fee goes NEGATIVE.
# Record today's behaviour; ask the product owner; fix it in a separate change.
self.assertEqual(late_fee(1, 5), -5)
suite = unittest.defaultTestLoader.loadTestsFromTestCase(CharacterizeLateFee)
result = unittest.TextTestRunner(stream=io.StringIO()).run(suite)
for name in sorted(n for n in dir(CharacterizeLateFee) if n.startswith("test_")):
print("pinned:", name)
print("ran", result.testsRun, "tests, failures:", len(result.failures), "errors:", len(result.errors))আউটপুট:
pinned: test_cap_is_200
pinned: test_five_taka_per_day
pinned: test_old_member_one_day_late
pinned: test_on_time_is_free
ran 4 tests, failures: 0 errors: 0
চারটি test-ই পাস করে, কারণ এগুলো আজকের আচরণ যেমন আছে তেমন লেখে: সময়মতো ফেরতে ০, দিনে ৫ টাকা (৪ দিনে ২০), সর্বোচ্চ ২০০, আর অবাক করা −৫. এখন কেউ function-টি refactor করলে এই জাল যেকোনো অনিচ্ছাকৃত বদল ধরবে। −৫ ঠিক করতে চাইলে আগে নিয়মটি নিশ্চিত হবে (ন্যূনতম ০?), তারপর ওই test-টি বদলে লাল, কোড বদলে সবুজ — একটি আলাদা fix: commit-এ। অন্য ধাঁচের বড় output-এর জন্য একই ধারণার নাম golden master.
প্র ২৯অভিজ্ঞ · আপনার কোড একটি তৃতীয় পক্ষের payment API ডাকে। কীভাবে test করবেন যাতে test দ্রুত হয়, আবার ভরসাও থাকে? (কঠিন)
শক্তিশালী উত্তর: স্তরে স্তরে। (১) নিজের একটি ছোট interface — PaymentGateway.charge(order) — আর তার পেছনে একটি adapter, যা বাইরের API-র খুঁটিনাটি জানে (hexagonal-এর ভাষায় driven adapter). (২) Business logic-এর unit test-এ সেই interface-এর fake বা stub — সফল, প্রত্যাখ্যাত, timeout — যাতে ব্যর্থতার পথগুলোও মিলিসেকেন্ডে test হয়। (৩) Adapter-এর কয়েকটি integration test provider-এর sandbox-এর বিরুদ্ধে, হয়তো রাতের build-এ, কারণ এগুলো ধীর আর বাইরের অবস্থার ওপর নির্ভর করে। (৪) Provider যদি নিজের দল হয়, তাহলে consumer-driven contract test. সাথে নকশার প্রশ্ন: retry হলে দুবার টাকা কাটবে না তো — idempotency key; আর webhook-এর পথ। Test-এ কখনো আসল টাকা বা আসল গ্রাহকের তথ্য নয়।
৬ · Architecture আর design — layered, hexagonal, MVC, coupling, monolith বনাম microservices, ADR আর একটি module নকশা
রিভিশন: পর্ব ২৫ · Coupling আর cohesion, পর্ব ৩৮ · Software architecture, পর্ব ৩৯ · MVC, MVP আর MVVM, পর্ব ৪০ · Clean আর hexagonal, পর্ব ২৯ · UML class diagram; বড় system-এর নকশা System Design কোর্সে
প্র ৩০শুরু · MVC-তে business logic কোথায় থাকবে? MVC আর MVVM-এর মূল তফাত কী? (সহজ)
শক্তিশালী উত্তর: Business logic model বা একটি service স্তরে — view-এ নয় (view শুধু দেখায়), controller-এ নয় (controller শুধু input নিয়ে ঠিক জায়গায় পাঠায়)। Controller-এ নিয়ম জমলে তৈরি হয় massive controller — test কঠিন, পুনর্ব্যবহার অসম্ভব। Classic MVC-তে model নিজের বদলের খবর view-কে দেয় (observer); web framework-এর “MVC” এর একটি রূপ, যেখানে controller একটি HTTP অনুরোধ সামলায়। MVVM-এ view model state আর command প্রকাশ করে, আর data binding নিজে থেকে screen মেলায় — তাই view model UI ছাড়াই test করা যায়। Two-way binding-এ guard না থাকলে বদলের অসীম চক্র তৈরি হতে পারে।
প্র ৩১মাঝারি · Hexagonal architecture (port আর adapter) কী সমস্যার সমাধান করে? Layered-এর সাথে তফাত কী? (মাঝারি)
শক্তিশালী উত্তর: Hexagonal architecture (Alistair Cockburn-এর প্রস্তাব) কেন্দ্রে application core রাখে — business নিয়ম, framework বা database-এর কোনো import ছাড়া। Core বাইরের জগতের সাথে কথা বলে নিজের সংজ্ঞা দেওয়া port (interface) দিয়ে; বাইরের প্রযুক্তি সেই port মানে adapter দিয়ে। যে core-কে চালায় (web, CLI, test) — driving adapter; যাকে core চালায় (database, SMS, payment) — driven adapter. Layered-এ নিয়ম নিচের database-এর ওপর নির্ভর করে; hexagonal-এ নির্ভরতা শুধু ভেতরের দিকে — এটি পুরো application-এ DIP-এর প্রয়োগ। লাভ: database ছাড়াই core-এর test, প্রযুক্তি বদল সস্তা। দাম: বেশি file, ডেটার রূপান্তর, indirection — চার field-এর CRUD app-এ প্রায়ই over-engineering.
ফলো-আপ:
- “Clean Architecture আর onion?” → একই মূল কথা — dependency rule: source code-এর নির্ভরতা শুধু ভেতরের বৃত্তের দিকে। নাম আর স্তরের সংখ্যা আলাদা।
প্র ৩২মাঝারি · Coupling আর cohesion কী? “Low coupling, high cohesion” কোডে কীভাবে দেখবেন বা মাপবেন? (মাঝারি)
শক্তিশালী উত্তর: Coupling — module-দের পারস্পরিক নির্ভরতা: একটি বদলালে কতগুলো বদলাতে হয়। Cohesion — একটি module-এর ভেতরের অংশগুলো কতটা এক উদ্দেশ্যে বাঁধা। চেনার উপায়: একটি feature-এর বদলে কতগুলো file ছুঁতে হয় (shotgun surgery মানে উচ্চ coupling); class-এর method-গুলো কি একই field-গুলো ব্যবহার করে (না করলে দুর্বল cohesion — LCOM মাপ এটাই গোনে); import-এর graph-এ চক্র আছে কি না। Robert C. Martin-এর package metric — instability I = Ce ÷ (Ca + Ce) — দেখায় কোন package-এর ওপর অনেকে নির্ভর করে (তাই সেটি স্থির আর abstract হওয়া উচিত)। Metric সতর্কসংকেত, রায় নয়। আর ভারসাম্য: অতিরিক্ত ভাগ করলে প্রতিটি কাজে দশটি module-এর মধ্যে লাফাতে হয় — সেটিও খারাপ।
প্র ৩৩মাঝারি · নতুন একটি product শুরু হচ্ছে — monolith না microservices? (মাঝারি)
শক্তিশালী উত্তর: সাধারণত একটি ভালোভাবে ভাগ করা modular monolith দিয়ে শুরু — একটি deploy, এক database, কিন্তু ভেতরে স্পষ্ট module-সীমানা আর নিয়ম (কোন module কাকে import করতে পারে)। Microservices স্বাধীন deploy আর আলাদা scaling দেয়, আর বড় অনেক দলকে আলাদা গতিতে চলতে দেয় — কিন্তু দাম network-এর ব্যর্থতা, distributed transaction, observability, আর অনেক service চালানোর খরচ। শুরুতে domain-এর সীমানা প্রায়ই ভুল বোঝা হয়; monolith-এ সীমানা বদলানো একটি refactor, microservices-এ একটি migration. ভুল ভাগের ফল distributed monolith — দুই জগতেরই খারাপটা। কখন ভাঙবেন: যখন দল বা deploy-এর চাপ সত্যিই আসে, আর module-এর সীমানা স্থির হয়েছে। বিস্তারিত trade-off: System Design · Microservices.
দুর্বল উত্তর দেখতে যেমন: “Microservices আধুনিক আর scalable, তাই শুরু থেকেই।” — দল, ট্রাফিক বা দামের কোনো প্রশ্ন ছাড়া।
প্র ৩৪মাঝারি · ADR কী, আর কেন লিখবেন? (মাঝারি)
শক্তিশালী উত্তর: Architecture Decision Record — একটি গুরুত্বপূর্ণ নকশার সিদ্ধান্তের ছোট লিখিত নথি: title, status (প্রস্তাবিত, গৃহীত, বাতিল, অন্য ADR দিয়ে প্রতিস্থাপিত), context (কোন চাপ আর বাধ্যবাধকতা), decision, আর consequences (কী পেলাম, কী দিলাম)। ধারণাটি Michael Nygard-এর ২০১১ সালের একটি blog লেখা থেকে জনপ্রিয়। এটি কোডের পাশে — যেমন docs/adr/ — Git-এ থাকে, তাই PR-এ review হয় আর history-তে থাকে। লাভ: এক বছর পরে নতুন সহকর্মী “কেন এমন?” জিজ্ঞেস করলে উত্তর লেখা আছে; পুরোনো সিদ্ধান্ত বদলাতে চাইলে বোঝা যায় কোন অনুমান বদলেছে। ADR পুরোনো হলে মোছা হয় না, নতুন ADR দিয়ে প্রতিস্থাপিত হয়।
প্র ৩৫অভিজ্ঞ · “একটি module নকশা করুন”: একটি কাল্পনিক অনলাইন বাজার অ্যাপের coupon বা ছাড় module. ২০ মিনিটে কীভাবে এগোবেন? (কঠিন)
এটি পুরো system design নয় — একটি process-এর ভেতরের module-এর class, interface আর test-এর নকশা। নমুনা উত্তর, ধাপে ধাপে:
- প্রশ্ন পরিষ্কার করা (৩ মিনিট): কী ধরনের ছাড় — শতাংশ, নির্দিষ্ট টাকা, “২টি কিনলে ১টি ফ্রি”? একটি অর্ডারে কয়টি coupon? মেয়াদ আর ব্যবহারের সীমা (প্রতি ব্যবহারকারী একবার)? ছাড় কি পণ্যের দামের নিচে যেতে পারে? ধরে নিচ্ছি: তিন ধরন, এক অর্ডারে একটি coupon, মেয়াদ আর জনপ্রতি সীমা, মোট কখনো ঋণাত্মক নয়।
- Domain-এর শব্দ:
Coupon(code, rule, মেয়াদ, সীমা),DiscountRule,Cart,Money— টাকা float নয়, পয়সায় পূর্ণসংখ্যা বা decimal-এর একটি value object. - Interface:
DiscountRule.apply(cart) → Money— তিনটি বাস্তবায়ন (strategy); নতুন ধরন মানে নতুন class (OCP).CouponService.redeem(code, cart, userId, now)— যাচাইয়ের ক্রম: আছে কি না → মেয়াদ → ব্যবহারকারীর সীমা → rule প্রয়োগ → ০-এর নিচে নয়। - Port:
CouponRepository(খোঁজা, ব্যবহার গোনা) আরClock— দুটিই interface, core-এর মালিকানায়; database আর আসল ঘড়ি adapter-এ (প্র ০৮, ২৭)। - Test-এর তালিকা: মেয়াদের ঠিক সীমানায় (শেষ মুহূর্তে বৈধ, এক সেকেন্ড পরে নয়), সীমা পূর্ণ, ছাড় দামের চেয়ে বড় (ফল ০), অচেনা code, তিনটি rule আলাদা আলাদা। সব fake repository আর stub clock দিয়ে, database ছাড়া।
- ঝুঁকি আর পরে: একই coupon দুটি অনুরোধে একসাথে — ব্যবহার গোনা atomic হতে হবে (database-এ শর্তসহ update); একাধিক coupon-এর মিশ্রণ এলে rule-এর ক্রম আর একটি chain বা composite. যা এখন বানাচ্ছি না: rule engine, admin UI — YAGNI.
দুর্বল উত্তর দেখতে যেমন: প্রশ্ন না করে সরাসরি “একটি CouponFactory, একটি CouponStrategy, একটি CouponObserver…” — pattern-এর তালিকা, requirement নয়; বা microservice আর message queue দিয়ে শুরু।
ফলো-আপ:
- “এখন ‘প্রথম ১০০ জন’-এর coupon চাই।” → সীমা জনপ্রতি নয়, মোট — counter-এর race আরও গুরুত্বপূর্ণ; repository-তে একটি atomic “ব্যবহার বাড়াও যদি < ১০০” — নকশার বাকিটা অপরিবর্তিত, কারণ নিয়মগুলো আলাদা।
৭ · Process আর delivery — Agile, Scrum, Kanban, estimation, CI/CD, DORA আর incident
রিভিশন: পর্ব ৫ · Agile আর Scrum, পর্ব ৬ · Kanban আর lean, পর্ব ৫৮ · Continuous integration, পর্ব ৫৯ · Continuous delivery আর DevOps, পর্ব ৬০ · Software estimation, পর্ব ৬২ · Risk আর quality; deploy-এর কৌশল System Design · Deployment-এ
প্র ৩৬শুরু · Agile আর Scrum কি একই? Scrum-এর role, event আর artifact বলুন। (সহজ)
শক্তিশালী উত্তর: না। Agile একটি মূল্যবোধের সেট — ২০০১ সালের Agile Manifesto-র ৪টি মূল্যবোধ আর ১২টি নীতি: ছোট ধাপে কাজ করা software, মতামত শুনে দিক বদল। Scrum সেই মূল্যবোধের একটি হালকা কাঠামো। Scrum Guide-এর ২০২০ সংস্করণ অনুযায়ী: তিনটি দায়িত্ব — Product Owner, Scrum Master, Developers; পাঁচটি event — Sprint (এক মাস বা কম), Sprint Planning, ১৫ মিনিটের Daily Scrum, Sprint Review, Sprint Retrospective; তিনটি artifact আর তাদের commitment — Product Backlog (Product Goal), Sprint Backlog (Sprint Goal), Increment (Definition of Done). Kanban, XP বা নিজের দলের মিশ্র পদ্ধতিও agile হতে পারে।
দুর্বল উত্তর দেখতে যেমন: “Agile মানে documentation নেই আর প্রতিদিন meeting.”
ফলো-আপ:
- “Daily Scrum কি manager-কে status report?” → না; Developers-দের নিজেদের পরিকল্পনা মেলানোর সময় — Sprint Goal-এর দিকে কী বাধা।
- “Scrum না Kanban?” → কাজ আগে থেকে জানা গেলে sprint-এর Scrum; support বা হঠাৎ আসা কাজে sprint ছাড়া Kanban, প্রতিটি কলামে WIP limit. Little-এর সূত্রে গড় cycle time = গড় WIP ÷ গড় throughput: দিনে ৩টি কাজ শেষ হয় আর একসাথে ১২টি চলে, তাহলে গড়ে ৪ দিন; WIP ৬-এ নামালে (throughput একই থাকলে) ২ দিন।
প্র ৩৭মাঝারি · Manager জিজ্ঞেস করলেন, “এই feature কতদিনে হবে?” আপনার হাতে খুব কম তথ্য। কী উত্তর দেবেন? (মাঝারি)
শক্তিশালী উত্তর: একটি সংখ্যা নয়, একটি পরিসর আর তার অনুমান। শুরুতে অনিশ্চয়তা অনেক বেশি (cone of uncertainty), তাই: “সবকিছু ঠিকঠাক গেলে ২ দিন, সম্ভবত ৪, payment-এর অংশে অজানা থাকলে ১২ পর্যন্ত। PERT-এর হিসাবে (O + 4M + P) ÷ ৬ = (২ + ১৬ + ১২) ÷ ৬ = ৫ দিন, বিচ্যুতি (১২ − ২) ÷ ৬ ≈ ১.৬৭ দিন। এক দিনের একটি spike করে payment-এর অংশ বুঝলে পরিসর অনেক ছোট হবে।” সাথে তিনটি আলাদা জিনিস আলাদা রাখুন: estimate (আমাদের অনুমান), target (ব্যবসার চাওয়া), commitment (যা কথা দিচ্ছি)। Agile দলে story point আর planning poker — সবাই একসাথে কার্ড উল্টান, যাতে প্রথম সংখ্যা সবাইকে টেনে না নেয়।
দুর্বল উত্তর দেখতে যেমন: চাপে পড়ে “দুই দিন” — আর পরে চুপচাপ overtime.
প্র ৩৮মাঝারি · Management দুটি দলের velocity তুলনা করে বলছে, “B দল কম কাজ করে।” আপনার মত? (মাঝারি)
শক্তিশালী উত্তর: তুলনাটি অর্থহীন। Velocity প্রতিটি দলের নিজের story point-এর মাপে — A দলের “৫” আর B দলের “৫” আলাদা একক, যেমন দুই দোকানের নিজস্ব “এক মুঠো”। Velocity পরিকল্পনার হাতিয়ার — পরের কয়েক sprint-এ কতটা নেওয়া যায় — পারফরম্যান্সের মাপ নয়। একে লক্ষ্য বানালে Goodhart-এর সূত্রে নষ্ট হয়: দল point ফুলিয়ে দেবে, সংখ্যা বাড়বে, কাজ নয়। দুই দলের তুলনা চাইলে ফল দেখুন — ব্যবহারকারীর কাছে পৌঁছানো মূল্য, lead time, ব্যর্থতার হার — আর দুই দলের কাজের ধরন যে আলাদা, তা মাথায় রেখে।
প্র ৩৯মাঝারি · Continuous integration, continuous delivery আর continuous deployment-এর পার্থক্য কী? (মাঝারি)
শক্তিশালী উত্তর: Continuous integration একটি অনুশীলন: সবাই ছোট পরিবর্তন অন্তত দিনে একবার mainline-এ মেলান, আর প্রতিটি পরিবর্তনে স্বয়ংক্রিয় build আর test চলে। তিন সপ্তাহের branch-এ pipeline চালানো CI নয়, শুধু “branch-এ test”. Continuous delivery: প্রতিটি সবুজ পরিবর্তন release-যোগ্য — production-এ পাঠানো একটি বোতামের ব্যবসায়িক সিদ্ধান্ত। Continuous deployment: সেই বোতামও নেই — সব check পার হলে পরিবর্তন নিজে থেকে production-এ। পেছনের নীতি: একবার build, একই artifact সব environment-এ; environment-ভেদে বদলায় শুধু config.
প্র ৪০মাঝারি · DORA-র চারটি metric কী, আর কেন জোড়ায় দেখতে হয়? (মাঝারি)
শক্তিশালী উত্তর: DORA metric (Forsgren, Humble ও Kim-এর Accelerate, ২০১৮-এর মাধ্যমে বহুল পরিচিত): গতির দুটি — deployment frequency আর lead time for changes (commit থেকে production); স্থিতির দুটি — change failure rate আর ব্যর্থতা থেকে ফেরার সময় (পরের সংস্করণে নাম failed deployment recovery time). জোড়ায় দেখতে হয় কারণ একটিকে একা ঠেলে অন্যটি নষ্ট করা সহজ — test বাদ দিয়ে দিনে দশবার deploy করলে গতি বাড়ে, failure rate-ও। তাদের জরিপে ভালো দলগুলো দুই দিকেই ভালো ছিল — গতি আর স্থিতি একে অপরের শত্রু নয়, ছোট batch দুটোকেই সাহায্য করে। সতর্কতা: এগুলো দলের নিজের প্রবণতা দেখার জন্য, মানুষ বা দলের র্যাংকিংয়ের জন্য নয়; আর metric-এর তালিকা ও নাম সংস্করণভেদে বদলেছে।
প্র ৪১অভিজ্ঞ · শুক্রবার সন্ধ্যায় deploy-এর পরে checkout-এ error বেড়ে গেছে। প্রথম ৩০ মিনিটে কী করবেন? (কঠিন)
শক্তিশালী উত্তর: প্রথম লক্ষ্য ক্ষতি থামানো, কারণ খোঁজা পরে। (১) স্বীকার আর ঘোষণা: incident চ্যানেলে “আমি দেখছি”; একজন সমন্বয়ক, যাতে পাঁচজন একসাথে পাঁচ দিকে না যান (incident response). (২) প্রভাব মাপা: কত শতাংশ অনুরোধ ব্যর্থ, কোন অঞ্চল বা platform. (৩) সাম্প্রতিক পরিবর্তনই প্রথম সন্দেহ — deploy-এর সময় আর error-এর শুরু মিললে আগে rollback বা feature flag বন্ধ; প্রমাণ সম্পূর্ণ হওয়ার অপেক্ষা নয়। (৪) Stakeholder-দের নিয়মিত সংক্ষিপ্ত হালনাগাদ। (৫) ফিরে এলে নিশ্চিত হওয়া — metric স্বাভাবিক। তারপর শান্ত মাথায় কারণ, আর পরের সপ্তাহে postmortem. যা করবেন না: production-এ সরাসরি অপরীক্ষিত hotfix, বা প্রথমেই কাকে দোষ দেওয়া যায় তা খোঁজা।
ফলো-আপ:
- “Rollback সম্ভব না হলে?” → যেমন database migration উল্টানো যায় না — তাই নকশাতেই expand-contract migration আর flag; তখন roll forward একটি ছোট, review-করা fix দিয়ে।
প্র ৪২অভিজ্ঞ · “Blameless postmortem” মানে কি কারও দায় নেই? একটি ভালো postmortem-এ কী থাকে? (কঠিন)
শক্তিশালী উত্তর: দায় আছে, কিন্তু খোঁজা হয় system-এর দুর্বলতা, মানুষের দোষ নয় (blameless culture). যুক্তি: মানুষ শাস্তির ভয় পেলে তথ্য লুকান, আর একই ফাঁক আবার কাউকে ফেলে দেয়। “Rahim ভুল command চালিয়েছেন” নয় — “একটি ভুল command production-এ এক ধাপে চালানো সম্ভব ছিল, কোনো নিশ্চিতকরণ বা অনুমতির স্তর ছাড়া”। একটি postmortem-এ থাকে: সারাংশ আর প্রভাব (কতজন, কতক্ষণ), সময়রেখা, কীভাবে টের পাওয়া গেল, অবদান রাখা কারণগুলো (একটিমাত্র “root cause” প্রায়ই সরলীকরণ), কী ভালো গেল, আর মালিক ও তারিখসহ action item. 5 whys কাজে লাগে, তবে একটি সরল শিকলে আটকে অনেক কারণের একটিকে বেছে নেওয়ার ঝুঁকি আছে।
৮ · আচরণগত প্রশ্ন আর STAR পদ্ধতি — আটটি প্রশ্ন, গোছানো নমুনা উত্তর
রিভিশন: পর্ব ৬৩ · দলগত কাজ, পর্ব ৬৪ · পেশাগত নৈতিকতা, পর্ব ৪৪ · Code review, পর্ব ৬৫ · ক্যাপস্টোন
“এমন একটি সময়ের কথা বলুন, যখন…” — এ ধরনের আচরণগত প্রশ্ন অতীতের আচরণ দিয়ে ভবিষ্যতের আচরণ বুঝতে চায়: দ্বিমতে, চাপে, ভুলে আপনি কী করেন। উত্তর গোছানোর পরিচিত ছক STAR:
| অংশ | কী বলবেন | মোটা দাগের ভাগ |
|---|---|---|
| S · Situation | প্রেক্ষাপট — কোথায়, কোন project, কী অবস্থা; দুই-তিন বাক্য | প্রায় ১৫% |
| T · Task | আপনার দায়িত্ব বা লক্ষ্য — দলের নয়, আপনার | প্রায় ১০% |
| A · Action | আপনি নিজে কী করলেন, কেন, কোন ক্রমে — “আমি”, শুধু “আমরা” নয় | প্রায় ৫০–৬০% |
| R · Result | ফল — সম্ভব হলে সংখ্যায়; আর কী শিখলেন, পরের বার কী আলাদা করবেন | প্রায় ২০% |
শতাংশগুলো মোটা দাগের, নিয়ম নয়; পুরো উত্তর সাধারণত দুই মিনিটের আশেপাশে। নিচের নমুনা উত্তরগুলো কাল্পনিক। মুখস্থ না করে ছকটি নিন, আর নিজের সত্যিকারের ঘটনা দিয়ে ৬–৮টি গল্প আগে থেকে তৈরি রাখুন — পড়াশোনার project, ক্যাপস্টোন (পর্ব ৬৫) বা open source-এর ঘটনাও চলে।
- Sপ্রেক্ষাপট
- Tআমার দায়িত্ব
- Aআমি যা করলাম
- Rফল, সংখ্যায়
- শিক্ষাপরের বার
| যা বলা হলো | |
| সময় | |
| যা এড়াবেন |
- S — প্রেক্ষাপট. দুই বাক্যে মঞ্চ সাজান: কোন project, কতজনের দল, আপনার ভূমিকা, আর সমস্যাটি কী। ইন্টারভিউয়ারের project-এর পুরো ইতিহাস দরকার নেই — শুধু এতটুকু, যাতে পরের অংশ বোঝা যায়। সময়ের চাপ (release-এর তিন সপ্তাহ আগে) উল্লেখ করলে ঘটনার গুরুত্ব বোঝা যায়।
- T — দায়িত্ব. এখানে আপনার নিজের লক্ষ্য স্পষ্ট করুন। “API-র নকশা আমার দায়িত্বে ছিল” — এই বাক্যটি দেখায় কেন সিদ্ধান্ত আনার দায় আপনার। দলের লক্ষ্য আর আপনার ভূমিকা আলাদা করে বলাই এই অংশের কাজ।
- A — কাজ. উত্তরের সবচেয়ে বড় অংশ। নির্দিষ্ট ক্রিয়া, ক্রমসহ: আলাদা বসা, আগে শোনা, মাপকাঠি লেখা, prototype দিয়ে মাপা, সিদ্ধান্ত ADR-এ লেখা। প্রতিটি ক্রিয়া একটি গুণ দেখায় — সম্মান, যুক্তি, ডেটা, স্বচ্ছতা। “আমি” বলুন; দলের কাজ হলে বলুন তার মধ্যে আপনার অংশ কোনটি।
- R — ফল. সম্ভব হলে একটি সংখ্যা: screen-প্রতি অনুরোধ ৬ থেকে ২, release সময়মতো। লক্ষ করুন, এখানে সহকর্মীর প্রস্তাবই জিতেছে — এটি দুর্বলতা নয়, বরং দেখায় আপনি অহং নয়, ফলকে গুরুত্ব দেন। সংখ্যা মনে না থাকলে বানাবেন না; “প্রায় অর্ধেক” বা গুণগত ফলই যথেষ্ট।
- শিক্ষা. অনেকে STAR-এর পরে একটি ছোট “L” (learning) যোগ করেন: কী শিখলেন, এখন কী আলাদা করেন। এটি বৃদ্ধির মানসিকতা দেখায় আর পরের follow-up-এর দরজা খোলে — “তারপর আবার এমন হয়েছে?” তৈরি থাকুন একটি দ্বিতীয় ছোট উদাহরণ নিয়ে।
প্র ৪৩আচরণগত · দলের একজন সহকর্মীর সাথে মতবিরোধ বা দ্বন্দ্বের একটি ঘটনা বলুন। (মাঝারি)
ইন্টারভিউয়ার যা শুনতে চান: আপনি কি শোনেন, ব্যক্তি নয় বিষয় নিয়ে কথা বলেন, আর দলের লক্ষ্যকে নিজের মতের ওপরে রাখেন।
নমুনা উত্তর (কাল্পনিক): S: “একটি কাল্পনিক রেস্তোরাঁ-অর্ডারের অ্যাপে আমি আর একজন সহকর্মী একই module-এ কাজ করতাম। তিনি প্রায় প্রতিদিন রাতে বড় বড় commit সরাসরি main-এ দিতেন, আর সকালে আমার কাজ conflict-এ আটকে যেত।” T: “আমার অংশ সময়মতো দেওয়া, আর দুজনের কাজের ধারা ঠিক করা দরকার ছিল — কিন্তু সম্পর্ক নষ্ট না করে।” A: “অভিযোগ না করে আগে তাঁকে চা খেতে ডাকলাম আর জিজ্ঞেস করলাম, তাঁর দিক থেকে সমস্যা কী। জানলাম, দিনে তিনি meeting-এ আটকে থাকেন, তাই রাতে একসাথে সব দেন। দুজনে মিলে প্রস্তাব দিলাম: ছোট branch, প্রতিদিন অন্তত একবার main থেকে update, আর ছোট PR-এ একে অপরের review. দলের পরের retrospective-এ নিয়মটি সবার জন্য প্রস্তাব করলাম।” R: “পরের দুই sprint-এ আমাদের merge conflict প্রায় থেমে গেল, আর একে অপরের PR দ্রুত review করায় দুজনেরই কাজ এগোল। শিখলাম — বিরক্তি জমার আগে কারণ জিজ্ঞেস করা।”
দুর্বল উত্তর দেখতে যেমন: “আমার কোনো দ্বন্দ্ব হয়নি।” — অবিশ্বাস্য; বা সহকর্মীকে খারাপ মানুষ হিসেবে দেখানো।
প্র ৪৪আচরণগত · আপনার একটি ভুল বা ব্যর্থতার কথা বলুন। (মাঝারি)
ইন্টারভিউয়ার যা শুনতে চান: সত্যিকারের ভুল, নিজের দায় স্বীকার, দ্রুত সারানো, আর এমন পরিবর্তন যাতে আবার না হয়।
নমুনা উত্তর (কাল্পনিক): S: “একটি কাল্পনিক অনলাইন কোচিং-প্ল্যাটফর্মে আমি পরীক্ষার ফলের পাতার একটি query দ্রুত করছিলাম।” T: “কাজটি আমার, আর release-এর আগে শেষ করার কথা ছিল।” A (ভুল): “তাড়াহুড়োয় test শুধু নিজের ছোট ডেটায় চালিয়ে merge করলাম। Production-এ যে শিক্ষার্থীর কোনো পরীক্ষা নেই, তাঁর পাতা খালি না দেখিয়ে error দিল।” A (সারানো): “Alert দেখে সাথে সাথে দলে জানালাম যে এটি আমার পরিবর্তন, ২০ মিনিটে revert করলাম, তারপর ঠিক ঘটনাটির একটি test লিখে fix দিলাম। Postmortem-এ প্রস্তাব করলাম, staging-এ বাস্তবধর্মী — কিন্তু নাম-পরিচয় মুছে ফেলা — ডেটার একটি সেট রাখা হোক।” R: “প্রায় ৪০ মিনিট কিছু শিক্ষার্থীর পাতা ভাঙা ছিল। এখন আমি edge case-এর তালিকা (খালি, একটি, অনেক) PR-এর বর্ণনায় লিখি, আর দলের PR template-এ এটি যোগ হয়েছে।”
দুর্বল উত্তর দেখতে যেমন: “আমার দুর্বলতা হলো আমি খুব বেশি পরিশ্রম করি।” — ছদ্ম-ভুল; বা দোষ test দলের ঘাড়ে।
প্র ৪৫আচরণগত · খুব কম সময়ে একটি কাজ শেষ করতে হয়েছিল — কী করেছিলেন? (মাঝারি)
ইন্টারভিউয়ার যা শুনতে চান: অগ্রাধিকার ঠিক করা, scope নিয়ে সৎ আলাপ, আর মান না হারিয়ে কী কাটা যায় তা বোঝা।
নমুনা উত্তর (কাল্পনিক): S: “ঈদের আগে একটি কাল্পনিক বাস-টিকিট অ্যাপে client হঠাৎ চাইলেন, এক সপ্তাহে ‘ফিরতি টিকিট একসাথে’ feature চাই — অনুমান ছিল তিন সপ্তাহ।” T: “আমি feature-এর মূল developer; দেখাতে হতো এক সপ্তাহে কী সম্ভব।” A: “Feature-কে ছোট টুকরোয় ভাঙলাম আর product owner-এর সাথে MoSCoW ধরে সাজালাম: ফিরতি টিকিট খোঁজা আর একসাথে payment — must; ছাড় আর আসন বদলানো — পরে। Payment-এর অংশে কোনো shortcut নিইনি, test-সহ। বাকি অংশ feature flag-এর পেছনে, যাতে অসম্পূর্ণ অংশ ব্যবহারকারী না দেখেন। প্রতিদিন বিকেলে client-কে পাঁচ লাইনের হালনাগাদ পাঠাতাম।” R: “ছয় দিনে মূল অংশ গেল, ঈদের ভিড়ে কাজ করল; বাকি অংশ পরের দুই সপ্তাহে। শিখলাম — ‘না’ নয়, ‘এতটুকু এখন, বাকিটা তখন’ বলা।”
দুর্বল উত্তর দেখতে যেমন: “তিন রাত না ঘুমিয়ে সব শেষ করেছি।” — বীরত্বের গল্প, প্রক্রিয়া নয়; ইন্টারভিউয়ার ভাবতে পারেন আপনি পরিকল্পনা বা আলাপ এড়িয়ে যান।
প্র ৪৬আচরণগত · Code review-এ একজন সিনিয়রের মন্তব্যের সাথে আপনি একমত ছিলেন না। কী করেছিলেন? (মাঝারি)
ইন্টারভিউয়ার যা শুনতে চান: পদমর্যাদা নয়, যুক্তি দিয়ে দ্বিমত; আর সিদ্ধান্ত হয়ে গেলে তা মেনে নেওয়া।
নমুনা উত্তর (কাল্পনিক): S: “একটি কাল্পনিক হাসপাতালের appointment-অ্যাপে আমার একটি PR-এ সিনিয়র সহকর্মী লিখলেন, সব ডাক্তারের তালিকা cache করতে।” T: “আমার মনে হয়েছিল, ডাক্তারের সময়সূচি দিনে বহুবার বদলায়, তাই cache-এ রোগী পুরোনো খালি সময় দেখবেন। PR-টি আমার, তাই সঠিক সমাধানে পৌঁছানো আমার দায়িত্ব।” A: “Comment-এ তর্ক না বাড়িয়ে উত্তর দিলাম: ‘একটি প্রশ্ন — সময়সূচি দিনে গড়ে ৪০ বার বদলায়, cache-এর মেয়াদ কত রাখলে ভুল সময় দেখানোর ঝুঁকি কম থাকবে?’ সাথে log থেকে বদলের সংখ্যাটি দিলাম। তিনি ১০ মিনিটের call চাইলেন; জানা গেল তাঁর আসল চিন্তা ছিল database-এর ভার। দুজনে মিলে ঠিক করলাম: ডাক্তারের নাম আর বিভাগ cache হবে, খালি সময় নয়।” R: “Database-এর ভার প্রায় অর্ধেক হলো, ভুল সময় দেখানোর অভিযোগ আসেনি। শিখলাম — মন্তব্যের পেছনের আসল উদ্বেগটি খুঁজে বের করা।”
ফলো-আপ:
- “আপনার যুক্তি শোনার পরেও তিনি একমত না হলে?” → “যুক্তি আর ডেটা দিয়ে একবার পরিষ্কার বলার পরে, দলের সিদ্ধান্ত-প্রক্রিয়া মেনে চলতাম — disagree and commit — আর ফল মাপার ব্যবস্থা রাখতাম, যাতে পরে ডেটা দেখে আবার ভাবা যায়।”
প্র ৪৭আচরণগত · একজন নতুন বা জুনিয়র সহকর্মীকে শেখানো বা সাহায্য করার একটি ঘটনা বলুন। (মাঝারি)
ইন্টারভিউয়ার যা শুনতে চান: ধৈর্য, শেখানোর কৌশল, আর অন্যকে স্বাবলম্বী করা — কাজ নিজে করে দেওয়া নয়।
নমুনা উত্তর (কাল্পনিক): S: “আমাদের দলে একজন intern যোগ দিলেন, যিনি Git-এ খুব ভয় পেতেন — একবার rebase-এ আটকে পুরো folder মুছে নতুন করে clone করেছিলেন।” T: “আমাকে তাঁর onboarding-এর দায়িত্ব দেওয়া হয়েছিল।” A: “প্রথম সপ্তাহে প্রতিদিন ৩০ মিনিট pair programming করলাম — keyboard তাঁর হাতে, আমি শুধু প্রশ্ন করতাম। একটি খেলার repository বানিয়ে তাঁকে ইচ্ছা করে conflict তৈরি আর সমাধান করতে দিলাম, আর reflog দিয়ে ‘হারানো’ commit ফেরানো দেখালাম, যাতে ভয় কাটে। তাঁর প্রথম তিনটি PR-এ মন্তব্যে কারণ লিখতাম, শুধু ‘এটা বদলান’ নয়। প্রশ্নগুলো জমিয়ে দলের wiki-তে একটি ‘নতুনদের প্রশ্ন’ পাতা বানালাম।” R: “তিন সপ্তাহ পরে তিনি নিজেই rebase করে PR দিচ্ছিলেন, আর পরের intern ওই wiki পাতা দিয়ে শুরু করেন। আমার শিক্ষা — উত্তর দেওয়ার চেয়ে প্রশ্ন করে শেখানো বেশি টেকে।”
প্র ৪৮আচরণগত · কাজের সময় এমন কিছু দেখেছেন যা নৈতিকভাবে ঠিক মনে হয়নি? কী করেছিলেন? (কঠিন)
ইন্টারভিউয়ার যা শুনতে চান: ঝুঁকি চেনা, তথ্য দিয়ে সঠিক মানুষকে জানানো, বিকল্প প্রস্তাব — নাটক বা চুপ থাকা নয়। পর্ব ৬৪-এর ACM/IEEE-CS code-এর প্রথম নীতিও জনস্বার্থ।
নমুনা উত্তর (কাল্পনিক): S: “একটি কাল্পনিক ঋণ-আবেদনের অ্যাপে marketing দল চাইল, ব্যবহারকারীর phone-এর পুরো contact list তুলে নিতে, ‘পরে কাজে লাগতে পারে’ বলে।” T: “Feature-টি আমার হাতে এসেছিল; আমার মনে হয়েছিল এটি ঘোষিত উদ্দেশ্যের বাইরে ব্যক্তিগত তথ্য নেওয়া।” A: “প্রথমে নিশ্চিত হলাম আমি ভুল বুঝছি না — product owner-কে লিখে জিজ্ঞেস করলাম উদ্দেশ্য কী। তারপর team lead-কে একটি ছোট লিখিত নোট দিলাম: কী ঝুঁকি (ব্যবহারকারীর আস্থা, privacy-র আইন ও platform-এর নীতির প্রশ্ন), আর একটি বিকল্প — শুধু ব্যবহারকারী নিজে যে দুজন reference বেছে দেন, তাঁদের নম্বর, স্পষ্ট সম্মতিসহ। আইনি বিষয় নিশ্চিত না হওয়ায় বললাম, এ নিয়ে compliance বা আইনি পরামর্শ নেওয়া দরকার।” R: “বিকল্পটি গৃহীত হলো। শিখলাম — ‘না’ বলার সাথে একটি কার্যকর বিকল্প দিলে আলাপ অনেক সহজ হয়।”
দুর্বল উত্তর দেখতে যেমন: “যা বলা হয় তাই করি, সিদ্ধান্ত management-এর।” — বা উল্টো, কাউকে না জানিয়ে নিজে থেকে কাজ বন্ধ করা।
প্র ৪৯আচরণগত · অল্প সময়ে একটি নতুন প্রযুক্তি শিখে কাজে লাগাতে হয়েছিল — কীভাবে শিখেছিলেন? (সহজ)
ইন্টারভিউয়ার যা শুনতে চান: শেখার একটি পদ্ধতি — শুধু “YouTube দেখেছি” নয় — আর শেখা জিনিস যাচাই করা।
নমুনা উত্তর (কাল্পনিক): S: “একটি কাল্পনিক কৃষি-বাজারদরের অ্যাপে আমাদের দল ঠিক করল offline-এ চলার জন্য mobile-এ একটি local database লাগবে; দলের কেউ আগে ব্যবহার করেননি।” T: “আমাকে দুই সপ্তাহে একটি কাজের সংস্করণ আর দলের জন্য নির্দেশিকা দিতে বলা হলো।” A: “প্রথম দুই দিন অফিসিয়াল documentation-এর শুরুর গাইড আর example; তারপর একটি ছোট খেলার অ্যাপ, যেখানে শুধু আমাদের তিনটি কঠিন প্রশ্ন পরীক্ষা করলাম — sync-এর conflict, schema বদল, বড় তালিকার গতি। প্রতিটির ফল একটি ছোট ADR-এ লিখলাম। AI-এর সাহায্য নিয়েছিলাম উদাহরণ বুঝতে, তবে প্রতিটি পরামর্শ documentation-এর সংস্করণ মিলিয়ে, কারণ কয়েকটি পুরোনো API-র ছিল।” R: “১২ দিনে offline তালিকা চলল; শুক্রবার দলের সামনে ২০ মিনিটের একটি ডেমো দিলাম। এখন নতুন কিছু শিখতে আগে ‘আমার আসল তিনটি প্রশ্ন কী’ লিখে নিই।”
প্র ৫০আচরণগত · সবচেয়ে কঠিন যে bug আপনি খুঁজে বের করেছেন, তার গল্প বলুন। (মাঝারি)
ইন্টারভিউয়ার যা শুনতে চান: অনুমান নয়, পদ্ধতি — পুনরুৎপাদন, পরিধি ছোট করা, প্রমাণ, আর একই রকম bug ঠেকানো।
নমুনা উত্তর (কাল্পনিক): S: “একটি কাল্পনিক বেতন-হিসাবের সফটওয়্যারে মাসের শেষ দিনে কয়েকজনের ওভারটাইম মাঝে মাঝে দুবার যোগ হতো — প্রতি মাসে নয়, আর local-এ কখনো নয়।” T: “আমাকে কারণ খুঁজে বের করার দায়িত্ব দেওয়া হলো।” A: “প্রথমে log মিলিয়ে দেখলাম, দুবার যোগ শুধু সেই মাসগুলোয়, যখন batch job চলার সময় server-এর সময়-অঞ্চল UTC আর হিসাব ঢাকার সময়ে — মাসের সীমানায় রাত ১২টার আশেপাশে একই দিন দুই মাসে পড়ত। একটি test-এ সময় ঠিক করে দিয়ে bug-টি পুনরুৎপাদন করলাম, আর কোন commit থেকে শুরু, তা git bisect দিয়ে খুঁজলাম। Fix-এর সাথে সব তারিখ এক জায়গায় একটি clock-এর মাধ্যমে, আর সীমানার চারটি test.” R: “পরের ছয় মাসে আর ঘটেনি। শেখা — ‘মাঝে মাঝে’ bug প্রায়ই সময়, ক্রম বা পরিবেশের পার্থক্য।”
ফলো-আপ:
৯ · ইন্টারভিউয়ারকে প্রশ্ন, live coding, take-home আর red flag
রিভিশন: পর্ব ৪১ · Clean code, পর্ব ৪৬ · Unit test, পর্ব ১৮ · ভালো commit; algorithm-এর প্রস্তুতি DSA কোর্সে
প্র ৫১শুরু · শেষে “আপনার কোনো প্রশ্ন আছে?” — কী জিজ্ঞেস করবেন? (সহজ)
শক্তিশালী উত্তর: “না, কোনো প্রশ্ন নেই” বলবেন না — এটি আপনার জন্যও প্রতিষ্ঠান যাচাইয়ের সুযোগ। দুই-তিনটি আগে থেকে তৈরি রাখুন, আর আলাপে যা শুনেছেন তার সাথে মিলিয়ে বাছুন:
- কাজের ধরন: “এই পদে প্রথম তিন মাসে একজন ভালো করছেন, তা কীভাবে বোঝা যাবে?” “একটি সাধারণ সপ্তাহে কোডিং, review আর meeting-এর ভাগ কেমন?”
- প্রকৌশলের চর্চা: “একটি পরিবর্তন commit থেকে production-এ যেতে সাধারণত কতক্ষণ লাগে?” “Code review আর test নিয়ে দলের নিয়ম কী?” “শেষ বড় incident-এর পরে কী বদলেছিল?”
- দল আর শেখা: “নতুনদের onboarding কীভাবে হয়?” “দলে দ্বিমত হলে সিদ্ধান্ত কীভাবে হয়?” “শেখার জন্য সময় বা সুযোগ কেমন?”
- চ্যালেঞ্জ: “এই মুহূর্তে দলের সবচেয়ে বড় প্রযুক্তিগত চ্যালেঞ্জ কী?”
প্রথম ধাপের আলাপে বেতন বা ছুটির খুঁটিনাটি সাধারণত HR-এর সাথে আলাদা আলোচনার বিষয়; প্রশ্ন করা দোষের নয়, তবে সময় আর ব্যক্তি বুঝে। যে প্রশ্নের উত্তর প্রতিষ্ঠানের website-এই আছে, তা জিজ্ঞেস না করাই ভালো।
প্র ৫২মাঝারি · Live coding-এ ভালো আচরণ কেমন? কোড লেখার আগে আর পরে কী করবেন? (মাঝারি)
শক্তিশালী উত্তর: Live coding-এ শুধু সঠিক উত্তর নয়, আপনি কীভাবে ভাবেন আর কাজ করেন তাও দেখা হয়। একটি ক্রম:
- বুঝে নেওয়া: প্রশ্নটি নিজের ভাষায় আবার বলুন; ইনপুটের আকার, খালি বা ভুল ইনপুট, কোন ভাষা — জিজ্ঞেস করুন। একটি ছোট উদাহরণ হাতে হাতে মেলান।
- পরিকল্পনা মুখে: আগে সরল সমাধান আর তার জটিলতা বলুন, তারপর উন্নতি — “আগে O(n²)-এ চালাই, তারপর hash map দিয়ে O(n).” Interviewer পথটি মানলে তবে লেখা শুরু।
- লেখার সময় জোরে ভাবা: দীর্ঘ নীরবতা নয়; ভালো নাম, ছোট helper function — এখানেও clean code দেখা হয়।
- নিজে test: শেষ করে নিজের উদাহরণ আর edge case (খালি, একটি উপাদান, সীমানা) দিয়ে কোডটি হাতে চালান — interviewer bug ধরার আগে নিজে ধরুন।
- আটকে গেলে: কোথায় আটকেছেন তা বলুন, একটি ছোট রূপ সমাধান করুন; ইঙ্গিত পেলে কৃতজ্ঞতার সাথে নিয়ে এগোন।
দুর্বল উত্তর দেখতে যেমন: প্রশ্ন শেষ হওয়ার আগেই লেখা শুরু, দশ মিনিট চুপ, তারপর “হয়ে গেছে” — কোনো test ছাড়া।
প্র ৫৩মাঝারি · Take-home assignment-এ কীভাবে জমা দিলে কাজটি পেশাদার দেখায়? (মাঝারি)
শক্তিশালী উত্তর: Take-home assignment আসলে একটি ছোট project — এখানে পুরো কোর্সের অভ্যাস একসাথে দেখা যায়:
- আগে প্রশ্ন: brief অস্পষ্ট হলে লিখে জিজ্ঞেস করুন; উত্তর না পেলে অনুমানগুলো README-তে লিখুন।
- Timebox: বলা সময় মেনে চলুন; বাড়তি feature নয়, বলা feature ভালোভাবে। যা বাদ দিলেন, README-র “সময় পেলে যা করতাম” অংশে।
- README: কীভাবে চালাবেন (এক-দুই command), কীভাবে test চালাবেন, নকশার মূল সিদ্ধান্ত আর trade-off, জানা সীমাবদ্ধতা।
- Test: মূল নিয়মের unit test আর অন্তত একটি integration test — সংখ্যার চেয়ে অর্থপূর্ণতা জরুরি।
- Git history: ছোট, অর্থপূর্ণ commit আর ভালো message — অনেকে history-ও পড়েন (ভালো commit নিয়ে পর্ব ১৮)। Secret,
.envবা বড় build-ফাইল commit নয়। - সততা: কোড নিজের বোঝা হতে হবে — follow-up আলাপে প্রতিটি লাইন ব্যাখ্যা করতে হতে পারে। AI বা বাইরের কোড ব্যবহারের নিয়ম brief-এ থাকলে মেনে চলুন, আর ব্যবহার করলে জানান; কপি করা কোডের licence মাথায় রাখুন।
দুর্বল উত্তর দেখতে যেমন: একটি zip, একটি বিশাল “final” commit, চালানোর কোনো নির্দেশনা নেই, আর চাওয়া হয়নি এমন microservice আর Kubernetes.
Red flag — যে বাক্যগুলো শুনলে অগভীর বোঝাপড়া ধরা পড়ে
নিচের বাক্যগুলো কোনো প্রসঙ্গে সত্যও হতে পারে। Red flag হয় তখন, যখন এগুলো প্রসঙ্গ, দাম বা কারণ ছাড়া বলা হয়।
| Red flag বাক্য | কেন অগভীর শোনায় | বদলে যা বলবেন |
|---|---|---|
| “SOLID সবসময় মানতে হয়।” | নীতিকে আইন বানানো | “Boundary-তে abstraction; স্থির, একক বাস্তবায়নে সরাসরি — বদল এলে extract.” |
| “Square is-a Rectangle, তাই inheritance.” | আচরণের চুক্তি যাচাই না করা | “Client-এর প্রতিশ্রুতি subtype রাখে কি না — setter থাকলে রাখে না।” |
| “এখানে factory, strategy আর observer দেব।” (প্রশ্নের শুরুতেই) | সমস্যার আগে pattern | “কোন দিকে বদল আসবে? দুটি বাস্তবায়ন হলে strategy, নইলে একটি function.” |
| “Singleton দিলেই হবে।” | লুকানো global state আর test-এর ক্ষতি উপেক্ষা | “একটি instance composition root-এ বানিয়ে constructor দিয়ে দেব।” |
| “আমাদের coverage ১০০%, তাই bug নেই।” | চলা আর যাচাই গুলিয়ে ফেলা | “Coverage দেখায় কোথায় test নেই; test কতটা ধরে, mutation score দিয়ে দেখি।” |
| “সব কিছু mock করি।” | Implementation-এ বাঁধা ভঙ্গুর test, mock drift | “Boundary-তে নকল, ভেতরে আসল ছোট object; বাইরের API নিজের adapter-এর পেছনে।” |
| “Flaky test আবার চালালেই পাস করে।” | লক্ষণ ঢাকা, আস্থা নষ্ট | “Quarantine আর মালিক; সময়, ক্রম বা ভাগ করা অবস্থা — কারণ ধরে সারানো।” |
| “পুরোনো কোড খারাপ, পুরোটা আবার লিখব।” | অলিখিত নিয়ম হারানোর ঝুঁকি | “আগে characterization test, তারপর strangler fig-এ ধাপে ধাপে।” |
| “শুরু থেকেই microservices.” | দাম না দেখে ফ্যাশন | “Modular monolith; দল আর deploy-এর চাপ এলে স্থির সীমানা ধরে ভাঙা।” |
| “Velocity বাড়ানোই লক্ষ্য।” | পরিকল্পনার মাপকে লক্ষ্য বানানো (Goodhart) | “ফল আর lead time দেখি; velocity শুধু পরের sprint-এর পরিকল্পনায়।” |
| “Bug-টা আসলে QA-র দোষ।” / “আগের দল সব নষ্ট করে গেছে।” | দোষারোপ, দলগত দায় না নেওয়া | “কোন ফাঁক দিয়ে এটি গেল, আর কী বদলালে আবার যাবে না।” |
| “আমি সবসময় ঠিক থাকি।” / “কোনো ভুল করিনি।” | শেখার মানসিকতার অভাব | একটি সত্যিকারের ভুল, দায় স্বীকার আর পরিবর্তনসহ (প্র ৪৪)। |
| “অমুক বড় প্রতিষ্ঠান এভাবেই করে।” | যাচাই-অযোগ্য দাবি, প্রসঙ্গ আলাদা | “প্রকাশিত এই লেখায় এমন নকশা আছে; আমাদের প্রয়োজনে মেলে কারণ …” |
প্রতিটি নীতির পাশে একটি “কখন নয়”। শক্তিশালী উত্তর প্রায় সবসময় শর্তসাপেক্ষ: “যদি দ্বিতীয় বাস্তবায়ন আসে, তাহলে…”, “যদি test-এ database না চাই, তাহলে…”। শর্তটি মুখে বললে ইন্টারভিউয়ার দেখেন আপনি দামটা চেনেন — আর প্রসঙ্গ বদলালে সিদ্ধান্তও বদলাতে প্রস্তুত।
১০ · AI-এর যুগে ইন্টারভিউর প্রস্তুতি, আর কোর্সের সমাপ্তি
AI একটি ধৈর্যশীল mock interviewer হতে পারে — অবিরাম follow-up দেয়, আপনার STAR উত্তরে ফাঁক খোঁজে, আপনার কোড review করে। কিন্তু software engineering-এর প্রশ্নে এর ভুলগুলো চেনা, আর সেগুলো আত্মবিশ্বাসের সাথে বলা হয়:
| AI-এর সাধারণ ভুল | কীভাবে ধরবেন |
|---|---|
| প্রতিটি নকশার প্রশ্নে pattern-এর পর pattern — over-engineering-কে “best practice” বলা | জিজ্ঞেস করুন: কোন বদলের আশঙ্কা এই abstraction দাবি করে? উত্তর না থাকলে বাদ |
| এমন test লেখা যা কিছুই যাচাই করে না, বা implementation-এর প্রতিচ্ছবি (একই সূত্র দিয়ে প্রত্যাশিত মান হিসাব) | কোডে একটি ভুল ঢুকিয়ে দেখুন test লাল হয় কি না — হাতে করা ছোট mutation test |
| দেখতে বিশ্বাসযোগ্য কিন্তু ভুল UML — ভুল তীরের মাথা, aggregation আর composition গুলিয়ে ফেলা | পর্ব ২৯-৩০-এর notation-এর সাথে প্রতিটি তীর মিলিয়ে নিন |
| Scrum Guide-এর পুরোনো নিয়ম, বা বানানো বই, বছর আর standard-এর নাম | মূল উৎস — Scrum Guide ২০২০, বইয়ের প্রকাশনার তথ্য — নিজে দেখে নিন |
| “অমুক প্রতিষ্ঠানের ইন্টারভিউতে এই প্রশ্ন আসে” — বানানো তালিকা আর প্রক্রিয়া | এ ধরনের দাবি উপেক্ষা করুন; ধারণা আর পদ্ধতি অনুশীলন করুন |
| চকচকে, বানানো সংখ্যা-ভরা STAR গল্প — যা আপনার নয় | শুধু নিজের সত্যিকারের ঘটনা; follow-up-এ বানানো গল্প ধরা পড়ে, আর তা অসৎও |
| Refactor-এর নামে আচরণ বদল — rounding, ক্রম বা edge case চুপচাপ বদলে যাওয়া | আগের আর পরের কোড একই ইনপুটে চালিয়ে ফল মেলান, এই পাতার block-গুলোর মতো |
AI-কে mock interviewer বানাতে চাইলে নিয়মগুলো স্পষ্ট দিন — একবারে একটি প্রশ্ন, আপনার উত্তরের পরেই মূল্যায়ন আর follow-up. প্রম্পটে নিজের কোনো ব্যক্তিগত তথ্য, আগের অফিসের কোড বা গোপন তথ্য দেবেন না।
AI-এর দেওয়া উত্তর বা কোড মুখস্থ করার আগে — চেকলিস্ট
কোর্সের সমাপ্তি — এখন আপনি যা পারেন
অভিনন্দন — ৭০টি পর্বের শেষ পাতায় পৌঁছেছেন। এই পাতার প্রতিটি প্রশ্নে পুরো কোর্স ফিরে ফিরে এসেছে: process আর Agile (মডিউল ১), Git-এর দলগত কাজ (২, ৩, ৯), requirement (৪), নকশার নীতি আর UML (৫), pattern (৬), architecture আর clean code (৭), testing (৮), আর delivery, estimation ও পেশাদারি (১০)। এখন আপনি:
অনুশীলন চালিয়ে যেতে: সপ্তাহে একটি mock — একজন বন্ধুকে ইন্টারভিউয়ার বানিয়ে এই পাতার যেকোনো পাঁচটি প্রশ্ন আর একটি STAR গল্প, ঘড়ি ধরে। Git-এর প্রশ্নের জন্য পর্ব ৬৯, হিসাবের জন্য পর্ব ৬৬, দ্রুত রিভিশনে পর্ব ৬৭ আর লিখিত-মৌখিকের জন্য পর্ব ৬৮. নিজের একটি ছোট project-এ পুরো পথটি আবার হাঁটুন (পর্ব ৬৫-এর চেকলিস্ট ধরে), কোনো open source project-এ ছোট একটি PR দিন, আর প্রকাশিত postmortem ও engineering লেখা পড়ুন — কোন সিদ্ধান্ত কেন, আর কী ভেঙেছিল। সার্টিফিকেট: ড্যাশবোর্ড থেকে মডিউল টেস্ট আর কোর্সের সমাপনী পরীক্ষা দিন; শর্ত পূরণ হলে সেখান থেকেই কোর্সের সার্টিফিকেট পাবেন।
নিজেকে যাচাই করুন
ইন্টারভিউর ধাঁচের ছোট প্রশ্ন — নীতি, pattern, test আর process. ভুল হলে কারণসহ ব্যাখ্যা পাবেন, আবার চেষ্টাও করতে পারবেন।
ক্রমDeploy-এর পরে production-এ error বেড়ে গেছে। একটি পরিণত দলের পদক্ষেপগুলো সঠিক ক্রমে সাজান (▲▼ চেপে সরান)
- 🔔 Alert স্বীকার করা আর একজন সমন্বয়ক ঠিক করা
- 📏 প্রভাব মাপা — কতজন, কোন অংশ
- ↩️ সাম্প্রতিক deploy rollback বা feature flag বন্ধ করে ক্ষতি থামানো
- ✅ Metric স্বাভাবিক হয়েছে কি না নিশ্চিত হওয়া
- 📝 দোষারোপহীন postmortem আর মালিকসহ action item
(সহজ) class Square extends Rectangle-এ setWidth(w) আর setHeight(h) দুটিই width ও height দুটিকে একই মান দেয়। একটি function r.setWidth(5); r.setHeight(4); return r.area(); একটি Square পেলে কী ফেরত দেবে?
- 20
- 25
- 16
- 9
(মাঝারি) OrderService-এর constructor-এর parameter-এর type MySQLRepository (একটি concrete class), আর main()-এ new OrderService(new MySQLRepository()) লেখা। কোন কথাটি সঠিক?
- Dependency injection আছে, কিন্তু dependency inversion নেই
- Dependency injection আর dependency inversion দুটিই আছে
- কোনোটিই নেই, কারণ new ব্যবহার হয়েছে
- তিনটি নাম একই জিনিস, তাই সবই আছে
(মাঝারি) একটি Order object-এ cancel()-এর আচরণ নির্ভর করে তার অবস্থার ওপর (Placed, Paid, Shipped), আর pay() ডাকলে object নিজেই Placed থেকে Paid অবস্থায় যায়। কোন pattern সবচেয়ে মানানসই?
- Strategy
- State
- Observer
- Decorator
(সহজ) withLogging(withRetry(client)) — প্রতিটি মোড়ক client-এর একই interface রাখে আর একটি আচরণ যোগ করে; মোড়কের ক্রম বদলালে ফল বদলায়। এটি কোন pattern?
- Adapter
- Facade
- Composite
- Decorator
(সহজ) একটি test-এ clock = { today: () => "2026-03-10" } বসানো হয়েছে। এটি শুধু একটি নির্দিষ্ট উত্তর দেয়, কোনো ডাক লিখে রাখে না বা নিজে কিছু যাচাই করে না। Meszaros-এর নামে এটি কী?
- Mock
- Stub
- Spy
- Fake
(মাঝারি) একটি test শুধু bill_total(orders) ডাকে, কোনো assertion নেই, আর coverage tool দেখাচ্ছে ১০০% line coverage. এ থেকে নিশ্চিতভাবে কী বলা যায়?
- Function-টি সঠিক ফল দেয়
- সব branch আর শর্তের মিশ্রণ যাচাই হয়েছে
- কোনো edge case বাদ পড়েনি
- শুধু এটুকু যে প্রতিটি লাইন অন্তত একবার চলেছে
(সহজ) একটি function ১৮ থেকে ৬০ বছর (দুটিই ধরে) বয়সে আবেদন গ্রহণ করে। Boundary value analysis-এ সীমানার কোন চারটি মান সবচেয়ে জরুরি?
- 17, 18, 60, 61
- 10, 35, 70, 90
- 18, 19, 59, 60
- 0, 18, 60, 100
(সহজ) নিচের কোন পরিবর্তনটি Fowler-এর সংজ্ঞায় refactoring?
- একটি নতুন payment পদ্ধতি যোগ করা
- ঋণাত্মক জরিমানা ০ করে দেওয়া
- একটি লম্বা function থেকে একটি অংশ আলাদা function-এ বের করা, সব ইনপুটে ফল একই রেখে
- টাকার হিসাব float থেকে Decimal-এ নেওয়া
(মাঝারি) একটি test CI-তে শুধু বিকেল ৩টা থেকে ৫টার মধ্যে চালালে পাস করে, অন্য সময় ফেল — কোড একই। সবচেয়ে সম্ভাব্য কারণ আর সমাধান কী?
- CI server ধীর — retry যোগ করা
- Test-গুলোর ক্রম — এলোমেলো ক্রমে চালানো
- Test-টি মুছে ফেলা
- কোড আসল ঘড়ির ওপর নির্ভর করে — ঘড়ি বাইরে থেকে দিয়ে test-এ সময় ঠিক করা
(মাঝারি) Test ছাড়া একটি পুরোনো function-এ characterization test লিখতে গিয়ে দেখলেন, একটি ইনপুটে এটি −৫ টাকা জরিমানা ফেরত দেয়, যা সম্ভবত ভুল। এখন কী করা উচিত?
- Refactor-এর মধ্যেই চুপচাপ ০ করে দেওয়া
- আপাতত −৫-ই test-এ ধরে রাখা, product owner-কে জিজ্ঞেস করা, আর fix আলাদা commit-এ
- ওই ইনপুটের test বাদ দেওয়া
- পুরো function নতুন করে লেখা
(মাঝারি) Singleton pattern-এর বিরুদ্ধে সবচেয়ে বড় সমালোচনা কোনটি?
- এটি একের বেশি instance বানাতে দেয় না
- এটি অনেক memory খরচ করে
- এটি লুকানো global নির্ভরতা আনে আর test-কে একে অন্যের ওপর নির্ভরশীল করে
- এটি কোনো OOP ভাষায় লেখা যায় না
(সহজ) STAR পদ্ধতির একটি আচরণগত উত্তরে কোন অংশটি সাধারণত সবচেয়ে বড় হওয়া উচিত?
- Action — আপনি নিজে কী করেছিলেন
- Situation — project-এর পুরো ইতিহাস
- Task — দলের সব লক্ষ্য
- Result — শুধু ফলের দীর্ঘ বর্ণনা
(মাঝারি) DORA-র চারটি metric-এর মধ্যে কোন দুটি স্থিতি (stability) মাপে?
- Change failure rate আর ব্যর্থতা থেকে ফেরার সময়
- Deployment frequency আর lead time for changes
- Velocity আর story point
- Test coverage আর lines of code
(সহজ) Scrum Guide ২০২০ অনুযায়ী Product Backlog-এর ক্রম ঠিক করার দায় (accountability) কার?
- Scrum Master
- Developers
- Project manager
- Product Owner
(মাঝারি) PaymentProvider interface-এ charge, refund, payout আর createSubscription. Cash-on-delivery-র adapter refund, payout আর createSubscription-এ “not supported” exception ছোড়ে, অথচ checkout শুধু charge ডাকে। সবচেয়ে সরাসরি কোন নীতির সমস্যা, আর fix কী?
- SRP — adapter-কে চারটি class-এ ভাগ করা
- ISP — client-এর প্রয়োজন ধরে ছোট role interface
- OCP — একটি switch যোগ করা
- DI — adapter-কে constructor দিয়ে দেওয়া
(মাঝারি) চারজনের একটি নতুন দল একটি নতুন product শুরু করছে; domain-এর সীমানা এখনো স্পষ্ট নয়। Architecture-এর সবচেয়ে যুক্তিসংগত শুরু কোনটি?
- প্রতিটি feature-এর জন্য একটি microservice
- ভেতরে কোনো ভাগ ছাড়া একটি বড় monolith
- স্পষ্ট module-সীমানাসহ একটি modular monolith
- সবকিছু serverless function-এ, কারণ তা সবচেয়ে আধুনিক
শব্দকোষ
লেখার মধ্যে ডটেড দাগ দেওয়া শব্দে মাউস রাখুন বা চাপুন — অর্থ দেখাবে। এই পর্বের সব শব্দ এখানে একসাথে:
মনে রাখুন
- উত্তরের ছক: এক বাক্যে সংজ্ঞা → ছোট উদাহরণ (smell → fix) → কী পাই, কী দিই → কখন নয় → কীভাবে যাচাই করব।
- SOLID আর pattern: LSP মানে আচরণের প্রতিশ্রুতি (Square ১৬, Rectangle ২০); DIP নীতি, DI কৌশল, IoC বড় ধারণা; pattern চেনা যায় intent দেখে — Strategy বাইরে থেকে বাছা, State ভেতর থেকে বদলায়; singleton-এর সমস্যা global পথ।
- Review আর refactor: নির্দিষ্ট, কারণসহ, লেবেল দেওয়া মন্তব্য; আগে সঠিকতা আর test. Refactor-এ আগে আর পরের ফল একই ইনপুটে মেলান; আচরণ বদল আলাদা PR-এ।
- Testing: stub উত্তর দেয়, spy লিখে রাখে, mock নিজে যাচাই করে, fake সরলভাবে কাজ করে; coverage দেখায় কী চলেছে, mutation দেখায় কী ধরা পড়ে; flaky test-এর সাধারণ কারণ ঘড়ি আর ভাগ করা অবস্থা; legacy-তে আগে characterization test.
- Architecture, process আর আচরণ: modular monolith দিয়ে শুরু, ADR-এ সিদ্ধান্ত; velocity তুলনার জন্য নয়, DORA জোড়ায়; incident-এ আগে ক্ষতি থামানো। আচরণগত উত্তরে STAR — Action সবচেয়ে বড়, গল্প নিজের আর সত্যিকারের।
আরও পড়ুন · ABCL TECH-এ আপনার পরবর্তী পদক্ষেপ
- পর্ব ৬৯ · Git ইন্টারভিউ — ধারণা, পরিস্থিতিভিত্তিক সমস্যা আর command-এর অনুশীলনআগের পর্বGit-এর সব ইন্টারভিউ প্রশ্ন — এই পাতার পরিপূরক।
- সফটওয়্যার ইঞ্জিনিয়ারিং ও Git · কোর্স হোম৭০টি পর্বপুরো কোর্সের রোডম্যাপ — দুর্বল মডিউলগুলো আবার দেখুন।
- পর্ব ৬৮ · লিখিত ও মৌখিক (ভাইভা) প্রশ্নপাশের অনুশীলনপরীক্ষার ধাঁচের সংজ্ঞা, তুলনা আর ব্যাখ্যা।
- পর্ব ৬৫ · ক্যাপস্টোন — requirement থেকে release পর্যন্ত একটি পূর্ণ projectপ্রজেক্টআচরণগত গল্প আর take-home-এর অভ্যাসের সবচেয়ে ভালো উৎস।
- System Design · ধারণাভিত্তিক ইন্টারভিউ প্রশ্নপাশের ট্র্যাকCache, database, queue আর consistency-র trade-off প্রশ্ন।
- ডেটা স্ট্রাকচার ও অ্যালগরিদমপাশের কোর্সLive coding-এর algorithm-অংশের প্রস্তুতি।
- Python প্রোগ্রামিংপাশের কোর্সClass, interface, exception আর unittest — এই পাতার কোডের ভাষা।
- JavaScript প্রোগ্রামিংপাশের কোর্সClosure, class আর module — pattern-এর function-ভিত্তিক রূপ।