সরাসরি পর্বে যান
পর্ব ৯৩ · ৯৩-এর মধ্যে · মডিউল ১৪

ফুল-স্ট্যাক ইন্টারভিউ — API, database, নকশা আর আচরণগত প্রশ্ন

Full-Stack Interview — API, Database, System Design and Behavioural Questions, with Live Coding and Debugging Rounds

A preparation page in Bangla for backend and full-stack interviews: 30 concept questions from HTTP and REST to deployment with linked lessons, covering auth, security, databases, transactions, caches, queues and testing, four live coding tasks in plain node:http such as a rate limiter and webhook verification, four backend debugging bugs, a checkout and notification system design with numbers, and STAR behavioural answers.

এই পর্বে যা শিখবেন

  • API, auth, database, cache, queue, test আর deployment-এর প্রশ্নে কারণ আর trade-off সহ গোছানো উত্তর দেওয়া
  • ছোট backend কাজ জোরে ভেবে লেখা: প্রশ্ন করা, সরল সমাধান, test, তারপর production-এর কথা
  • Race, ফাঁস হওয়া stack trace, মালিকানার যাচাই না থাকা আর ধীর query-কে প্রমাণসহ ধরা ও সারানো
  • একটি system design প্রশ্নে চাহিদা থেকে সংখ্যা, সংখ্যা থেকে নকশা, আর নকশা থেকে trade-off-এ যাওয়া
  • নিজের প্রকল্পের সত্যি ঘটনা STAR কাঠামোয় বলা, আর ইন্টারভিউয়ারকে ভালো প্রশ্ন করা

এই পাতা সম্পর্কে: প্রশ্নগুলো কোনো নির্দিষ্ট কোম্পানি, পরীক্ষা বা নিয়োগ থেকে নেওয়া নয়। এ ধরনের প্রশ্ন backend আর ফুল-স্ট্যাক ইন্টারভিউতে প্রায়ই আসে, তবে আপনার ইন্টারভিউ ভিন্ন হতে পারে। “বাজারঘর” আর সব মানুষের নাম কাল্পনিক। চালানো যায় এমন ব্লকগুলোর আউটপুট সত্যিই চালিয়ে নেওয়া, আর তাদের ডেটা নমুনা। React আর frontend-এর প্রশ্ন আছে পর্ব ৯২ · React আর frontend ইন্টারভিউ-তে; লিখিত আর মৌখিক প্রশ্ন পর্ব ৯১-এ।

রেস্তোরাঁয় নতুন রাঁধুনি নেওয়ার সময় মালিক শুধু জিজ্ঞেস করেন না, “বিরিয়ানি কাকে বলে?” তিনি বলেন, “রান্নাঘরে ঢুকুন, এক হাঁড়ি রাঁধুন। আর ভিড়ের রাতে একশো প্লেট একসাথে এলে কী করবেন?” ফুল-স্ট্যাক ইন্টারভিউও এমন। সংজ্ঞা জানা শুরু মাত্র; দেখা হয় চলার মতো কোড লেখা, ভাঙা কোডে কারণ খোঁজা, আর চাপে ব্যবস্থাটি কেমন আচরণ করবে তা ভেবে বলা।

প্রশ্নের কঠিনতার চিহ্ন: (সহজ), (মাঝারি), (কঠিন). প্রতিটি উত্তরের শেষে “রিভিশন” লেখা পর্বে সেই বিষয়ের পূর্ণ ব্যাখ্যা আর আসল run আছে।

১ · Backend আর ফুল-স্ট্যাক ইন্টারভিউ সাধারণত কীভাবে চলে

রিভিশন: পর্ব ০১, পর্ব ০২ · একটি অনুরোধের পুরো যাত্রা, পর্ব ৮৮ · ক্যাপস্টোন

ধাপ আর নাম প্রতিষ্ঠানভেদে আলাদা, তাই নিচের ছবিটি মোটা দাগের। ছোট দলে প্রায়ই একটি বা দুটি আলাপেই সব হয়; বড় প্রতিষ্ঠানে ধাপ বেশি, আর প্রতিটি আলাদা জিনিস মাপে। আবেদনের আগে job description পড়ুন: “Node.js, PostgreSQL, REST” লেখা থাকলে অংশ ২ থেকে ৪ আর ৬ বেশি কাজে দেবে; “scale” বা “distributed” থাকলে অংশ ৮.

ইন্টারভিউর সাধারণ ধাপ আর প্রতিটিতে যা দেখা হয়

A common interview loop; real loops vary by employer

ফুল-স্ট্যাক ইন্টারভিউর সাধারণ ধাপ ১ · প্রাথমিক আলাপ ১৫–৩০ মিনিট আগ্রহ, অভিজ্ঞতা, মিল অংশ ৯ ২ · ধারণার প্রশ্ন HTTP, auth, DB কারণ আর trade-off বোঝেন? অংশ ২–৫ ৩ · Live coding বা বাড়ির কাজ চলার মতো কোড, test, ভাবনা অংশ ৬ ৪ · Debugging ভাঙা কোড বা log প্রমাণ ধরে কারণ খোঁজা অংশ ৭ ৫ · System design ৪৫–৬০ মিনিট সংখ্যা থেকে নকশা, দাম অংশ ৮ ৬ · আচরণগত আলাপ STAR, প্রকল্প দলে কাজ, ভুল থেকে শেখা অংশ ৯ সময় আর ক্রম শুধু উদাহরণ; প্রতিষ্ঠানভেদে আলাদা।
ছবি ৯৩.১ — প্রতিটি ধাপ আলাদা দক্ষতা মাপে। একটিতে দুর্বল হলে অন্যটিতে শক্ত হয়ে পুষিয়ে দেওয়া যায়, তাই প্রতিটির জন্য আলাদা অনুশীলন করুন।

সব ধাপে একটি জিনিস সমান কাজে দেয়: জোরে ভাবা। “আগে সরল সমাধান লিখছি, পরে concurrency দেখব” বললে ইন্টারভিউয়ার জানেন আপনি ঝুঁকিটি চেনেন; চুপচাপ লিখলে সেই তথ্য হারায়।

প্র ০“নিজের সম্পর্কে বলুন।” প্রথম প্রশ্নে কী বলবেন? (সহজ)

দুর্বল উত্তর: জন্মস্থান থেকে শুরু করে পাঁচ মিনিটের জীবনী, অথবা “আমি একজন passionate developer.” কেন দুর্বল: কোনো তথ্য নেই, যাচাই করার মতো কিছু নেই।

শক্ত উত্তর: এক মিনিটে তিন অংশ। বর্তমান: “আমি Node, PostgreSQL আর React দিয়ে ফুল-স্ট্যাক কাজ শিখছি।” প্রমাণ: “একটি অনলাইন দোকান শেষ পর্যন্ত বানিয়েছি: session login, transaction-এ stock, সই করা webhook, আর প্রতিটি স্তরে test.” দিক: “এখন এমন দলে কাজ করতে চাই যেখানে backend-এর নির্ভরযোগ্যতা নিয়ে ভাবতে হয়।” তারপর থামুন, যাতে তিনি যেকোনো অংশ ধরে প্রশ্ন করতে পারেন।

২ · HTTP আর API নকশার প্রশ্ন

রিভিশন: পর্ব ৩২, পর্ব ৩৩, পর্ব ৩৪, পর্ব ৩৫, System Design · REST API নকশা

প্র ১বাজারঘরে একটি অর্ডার বাতিলের endpoint নকশা করুন। (সহজ)

দুর্বল উত্তর: “GET /cancelOrder?id=2041.” কেন দুর্বল: GET-এর প্রতিশ্রুতি হলো কিছু বদলাবে না; crawler, prefetch বা cache এটিকে নিজে থেকে ডেকে অর্ডার বাতিল করে দিতে পারে।

শক্ত উত্তর: REST-এ URL-এ বিশেষ্য, method-এ ক্রিয়া। বাতিল একটি অবস্থার বদল, তাই দুই পথ: PATCH /api/orders/2041 body-তে {"status":"CANCELLED"}, অথবা বাতিল যদি নিজেই একটি জিনিস (কারণ, কে, কখন) হয় তবে POST /api/orders/2041/cancellation. Server যাচাই করবে মালিক কে আর অবস্থা বাতিলের যোগ্য কি না; পাঠানো অর্ডারে 409. সফল হলে 200 আর নতুন representation. রিভিশন: পর্ব ৩২.

প্র ২400, 401, 403, 404, 409 আর 422-এর পার্থক্য একটি করে উদাহরণে বলুন। (সহজ)

দুর্বল উত্তর: “সব ভুলে 400 বা 500 দিই, বার্তায় লিখে দিই কী হয়েছে।” কেন দুর্বল: status code পড়ে browser, retry library, cache আর monitoring; সবাইকে একই code দিলে তারা ভুল আচরণ করে।

শক্ত উত্তর: তিনটি প্রশ্ন: কাজটি হয়েছে? ভুল কার? আবার পাঠালে লাভ আছে? 400: JSON-ই ভাঙা। 401: login নেই। 403: login আছে, কিন্তু customer হয়ে admin-এর কাজ। 404: জিনিসটি নেই, অথবা অন্যের অর্ডার, যার অস্তিত্বও জানাতে চাই না। 409: অবস্থার দ্বন্দ্ব, যেমন stock শেষ। 422: গড়ন ঠিক, কিন্তু মান ভুল, যেমন qty: -2. রিভিশন: পর্ব ৩৩.

প্র ৩API-র error response দেখতে কেমন হওয়া উচিত? (মাঝারি)

দুর্বল উত্তর: “{"error": err.message}, আর debugging-এর জন্য stack-টাও পাঠাই।” কেন দুর্বল: database-এর বার্তা, টেবিলের নাম আর ফাইলের path বাইরে যায়, আর client-এর কোড ভাঙে যখনই বার্তার লেখা বদলায়।

শক্ত উত্তর: একটি চুক্তি: যন্ত্রের জন্য স্থির code (OUT_OF_STOCK), মানুষের জন্য অনুবাদযোগ্য বার্তা, form-এর জন্য ঘরভিত্তিক ভুলের তালিকা, আর একটি trace id, যা দিয়ে log-এ পুরো গল্প পাওয়া যায়। মান হিসেবে problem details (RFC 9457, application/problem+json). Stack trace শুধু server-এর log-এ। অংশ ৭-এ এর একটি আসল run আছে। রিভিশন: পর্ব ৩৩, পর্ব ২৮.

প্র ৪Idempotent মানে কী? POST-এর retry কেন বিপজ্জনক, আর কীভাবে নিরাপদ করবেন? (মাঝারি)

দুর্বল উত্তর: “Idempotent মানে একই উত্তর আসে।” কেন দুর্বল: উত্তর নয়, প্রভাব। DELETE দ্বিতীয়বার 404 দিতে পারে, তবু সেটি idempotent.

শক্ত উত্তর: Idempotent মানে একবার বা দশবার পাঠালেও server-এর অবস্থায় প্রভাব একই। GET, PUT, DELETE এমন; POST নয়। ক্রেতা “অর্ডার দিন” চাপলেন, উত্তরটি network-এ হারাল, app আবার পাঠাল: দুটি অর্ডার, দুবার টাকা। সমাধান Idempotency-Key: client প্রতিটি নতুন কাজের জন্য একটি এলোমেলো key পাঠায়, server key আর body-র fingerprint জমায়, একই key ফিরলে জমানো উত্তর দেয়। একই key ভিন্ন body-তে 422, আগেরটি চলমান থাকলে 409. অংশ ৬-এ এটি লিখবেন। রিভিশন: পর্ব ৩৩, পর্ব ৬৫.

প্র ৫১০ লাখ পণ্যের তালিকা কীভাবে পাতায় ভাগ করবেন? Offset আর cursor-এর তুলনা করুন। (মাঝারি)

দুর্বল উত্তর: “LIMIT 20 OFFSET n, আর মোট সংখ্যা দেখাতে প্রতিবার COUNT(*).” কেন দুর্বল: গভীর পাতায় ডেটাবেস আগের সব সারি পেরিয়ে যায়, আর মাঝে নতুন সারি এলে একই পণ্য দুবার দেখা যায়।

শক্ত উত্তর: Offset সহজ, “পাতা ৭-এ যান” দেয়, ছোট তালিকায় ঠিক। বড় ও বদলাতে থাকা তালিকায় cursor (keyset): আগের পাতার শেষ সারির (created_at, id) ধরে WHERE (created_at, id) < (?, ?), সাথে মেলানো composite index. ক্রম অনন্য হতে হবে, তাই শেষে id. Cursor opaque রাখুন, আর hasMore-এর জন্য limit + 1 আনুন। দাম: যেকোনো পাতায় লাফ দেওয়া যায় না। রিভিশন: পর্ব ৩৪.

প্র ৬React form-এ তো যাচাই আছেই। Server-এ আবার কেন? (সহজ)

দুর্বল উত্তর: “TypeScript-এ type দেওয়া আছে, তাই ভুল ডেটা আসবে না।” কেন দুর্বল: type চলার সময়ে কিছুই আটকায় না, আর যে কেউ curl দিয়ে form ছাড়াই অনুরোধ পাঠাতে পারে।

শক্ত উত্তর: Client-এর validation সুবিধার জন্য, server-এরটি নিরাপত্তার জন্য। Body, params, query আর header, চারটিই বাইরের লেখা। একটি schema (যেমন Zod) দিয়ে server-এ যাচাই, প্রতিটি string আর array-তে ওপরের সীমা, অজানা key বাদ, যাতে role: "admin" পাঠিয়ে mass assignment না হয়। গড়ন ভুল হলে 422; ব্যবসার নিয়ম (stock নেই) service-এ, 409. একই schema দুই দিকে ভাগ করা যায়। রিভিশন: পর্ব ২৭, পর্ব ২১.

প্র ৭একটি চালু API-তে price field-এর নাম বদলে pricePaisa করতে চান। কীভাবে? (মাঝারি)

দুর্বল উত্তর: “নাম বদলে deploy করে দেব, frontend-ও একসাথে বদলাব।” কেন দুর্বল: পুরোনো mobile app, cache-এ থাকা পাতা আর অন্যের integration একসাথে বদলায় না।

শক্ত উত্তর: এটি একটি breaking change. আগে যোগ করুন, পরে মুছুন: কিছুদিন দুটি field-ই পাঠান, docs আর OpenAPI-তে পুরোনোটিকে deprecated লিখুন, log দিয়ে দেখুন কে এখনো পুরোনোটি পড়ছে, তারপর ঘোষিত তারিখে মুছুন। বড় বদলে নতুন version (/v2 বা header). চুক্তি OpenAPI-তে থাকলে CI-তে diff tool breaking change ধরতে পারে। রিভিশন: পর্ব ৩৫.

প্র ৮REST, GraphQL না tRPC: বাজারঘরের জন্য কোনটি? (মাঝারি)

দুর্বল উত্তর: “GraphQL আধুনিক, তাই GraphQL.” কেন দুর্বল: প্রযুক্তির নাম কোনো যুক্তি নয়; প্রশ্ন হলো কারা API ব্যবহার করবে।

শক্ত উত্তর: এ নিয়ে দলগুলোর মত আলাদা, তাই শর্তসহ বলুন। একটি TypeScript দল, একটি Next.js app, বাইরের কোনো client নেই: tRPC-তে type দুই দিকে ভাগ হয়, চুক্তি লেখার খরচ কম। অনেক রকম client, প্রত্যেকে ভিন্ন field চায়: GraphQL, তবে N+1, query-র খরচের সীমা আর field-স্তরের অনুমতি সামলাতে হয়। বাইরের partner, cache আর সরল tooling দরকার: REST আর OpenAPI. বাজারঘরে public API REST, ভেতরের admin-এ tRPC চলতে পারে। রিভিশন: পর্ব ৩৬, পর্ব ৩৭.

৩ · Authentication, অনুমতি আর ওয়েবের নিরাপত্তা

রিভিশন: পর্ব ৪৫, পর্ব ৪৬, পর্ব ৪৭, পর্ব ৪৮, পর্ব ৪৯, পর্ব ৫০, তত্ত্ব: Cybersecurity · Session আর token

প্র ৯Session-ভিত্তিক login না JWT? (মাঝারি)

দুর্বল উত্তর: “JWT, কারণ stateless আর scale করে।” কেন দুর্বল: একটি সাধারণ web app-এর জন্য এটি প্রায়ই বাড়তি জটিলতা, আর logout বা role বদল তখন কঠিন হয়।

শক্ত উত্তর: Session-এ cookie-তে শুধু একটি এলোমেলো id, তথ্য server-এর store-এ; server যেকোনো সময় session শেষ করতে পারে। JWT-এ তথ্য token-এর ভেতরে সই করা, তাই প্রতিটি অনুরোধে database লাগে না, কিন্তু মেয়াদের আগে বাতিল করতে denylist বা ছোট মেয়াদ লাগে। একটি browser app আর একটি server হলে session সহজ; mobile app, আলাদা API বা অনেক service হলে JWT মানায়। বাজারঘরের ক্যাপস্টোনে session বেছে নেওয়া হয়েছিল, এই কারণে। রিভিশন: পর্ব ৪৫, পর্ব ৪৬.

প্র ১০Browser-এ token কোথায় রাখবেন, আর refresh token কীভাবে কাজ করে? (কঠিন)

দুর্বল উত্তর: “localStorage-এ, সহজে পড়া যায়।” কেন দুর্বল: পাতায় একটি XSS হলেই যেকোনো script token পড়ে নিয়ে যেতে পারে।

শক্ত উত্তর: Refresh token থাকে HttpOnly, Secure, SameSite cookie-তে, যা JavaScript পড়তে পারে না; ছোট মেয়াদের access token memory-তে। প্রতিবার refresh-এ নতুন refresh token আসে আর পুরোনোটি বাতিল হয় (rotation); database-এ শুধু hash. পুরোনোটি আবার এলে চুরির লক্ষণ, তাই পুরো family বাতিল। React-এ একসাথে তিনটি অনুরোধ 401 পেলে refresh যেন একবারই চলে, নইলে ব্যবহারকারী নিজেই logout হন। রিভিশন: পর্ব ৪৬.

প্র ১১“Google দিয়ে login” ভেতরে কীভাবে কাজ করে? (মাঝারি)

দুর্বল উত্তর: “Google একটি token দেয়, সেটি দিয়েই অ্যাপ চালাই।” কেন দুর্বল: provider-এর token আপনার session নয়, আর state বা nonce না মেলালে অন্যের login আপনার ব্রাউজারে ঢুকিয়ে দেওয়া যায়।

শক্ত উত্তর: OAuth ২.০-এর authorization code flow, পরিচয়ের জন্য তার ওপর OpenID Connect. অ্যাপ ব্যবহারকারীকে provider-এ পাঠায় state, nonce আর PKCE-এর challenge সহ; ফিরে আসা code-কে server বদলায় token-এ, verifier দিয়ে; ID token যাচাই করে sub দিয়ে নিজের user খোঁজে, তারপর নিজের session বানায়। পরিচয়ের চাবি sub, email নয়। হাতে না লিখে পরীক্ষিত library ব্যবহার করুন। রিভিশন: পর্ব ৪৭, Cybersecurity · OAuth আর OIDC.

প্র ১২GET /api/orders/2042-এ login করা যেকোনো ক্রেতা যেকোনো অর্ডার দেখতে পান। কী ভুল, আর কীভাবে সারাবেন? (মাঝারি)

দুর্বল উত্তর: “ID-গুলো UUID করে দেব, তাহলে কেউ অনুমান করতে পারবে না।” কেন দুর্বল: অনুমান কঠিন হয়, কিন্তু link ফাঁস হলে কেউ থামায় না। এটি অনুমতির বিকল্প নয়।

শক্ত উত্তর: এটি IDOR, API-র ভাষায় BOLA: পরিচয় আছে, মালিকানার যাচাই নেই। সমাধান query-র ভেতরে: WHERE id = ? AND user_id = ?, আর না পেলে 404. আনার পরে যাচাই নয়, আনার সময়েই সীমা। একটি test matrix রাখুন: দুই ক্রেতা, একজন admin, প্রতিটি route. React-এ বোতাম লুকানো শুধু UX. অংশ ৭-এ এই বাগটি চালিয়ে দেখবেন। রিভিশন: পর্ব ৪৮, Cybersecurity · Broken access control.

প্র ১৩CORS কী রক্ষা করে, আর কী করে না? (মাঝারি)

দুর্বল উত্তর: “CORS error এলে Access-Control-Allow-Origin: * দিয়ে দিই, আর CORS আমাদের API-কে হামলা থেকে বাঁচায়।” কেন দুর্বল: দুটি ধারণাই ভুল।

শক্ত উত্তর: Browser নিজে same-origin নীতিতে অন্য origin-এর উত্তর পড়তে দেয় না। CORS হলো server-এর দেওয়া অনুমতি, কোন origin সেই উত্তর পড়তে পারবে। এটি browser-এর নিয়ম; curl বা অন্য server-কে আটকায় না, আর অনুরোধ পাঠানো বন্ধও করে না, শুধু পড়া। তাই এটি authentication-এর বিকল্প নয়। Cookie-সহ অনুরোধে * চলে না; নির্দিষ্ট origin-এর allowlist দিন। রিভিশন: পর্ব ২৬, পর্ব ৫০.

প্র ১৪CSRF কখন ঝুঁকি, আর SameSite=Lax কি যথেষ্ট? (কঠিন)

দুর্বল উত্তর: “আমরা JSON API, তাই CSRF হয় না।” কেন দুর্বল: cookie দিয়ে login হলে browser অন্য সাইটের পাঠানো অনুরোধেও cookie জুড়ে দিতে পারে।

শক্ত উত্তর: CSRF-এর শর্ত: login cookie-তে, আর অবস্থা বদলানো অনুরোধ অন্য পাতা থেকে পাঠানো যায়। SameSite বড় বেড়া, কিন্তু একই site-এর অন্য subdomain থেকে আসা অনুরোধে Lax কাজ করে না; পর্ব ৫০-এর ল্যাবে ঠিক এটি দেখা গেছে। তাই অবস্থা বদলানো route-এ CSRF token বা Sec-Fetch-Site যাচাই, আর GET কখনো অবস্থা বদলাবে না। Bearer token header-এ পাঠালে CSRF-এর ঝুঁকি কম, তবে তখন XSS-এর দিকে নজর বাড়ে। রিভিশন: পর্ব ৫০, Cybersecurity · CSRF আর cookie.

প্র ১৫Password কীভাবে রাখবেন, আর login-এ brute force কীভাবে ঠেকাবেন? (মাঝারি)

দুর্বল উত্তর: “SHA-256 করে রাখি, আর ৫টি ভুলে account lock.” কেন দুর্বল: দ্রুত hash অনুমানকেও দ্রুত করে; আর lockout দিয়ে অন্য কেউ আসল মালিককে বাইরে আটকে রাখতে পারেন।

শক্ত উত্তর: ধীর, salt-সহ password hash: argon2id বা scrypt. Login-এ rate limit, IP আর account মিলিয়ে, আর lockout-এর বদলে বাড়তে থাকা অপেক্ষা (backoff). জানা আর অজানা email-এ একই বার্তা, একই সময়, যাতে কে আছেন তা বোঝা না যায়। ঝুঁকির কাজে MFA. অংশ ৬-এ একটি rate limiter লিখবেন। রিভিশন: পর্ব ৪৫, পর্ব ৪৯.

৪ · Database, ORM, transaction আর cache

রিভিশন: পর্ব ৩৮, পর্ব ৩৯, পর্ব ৪০, পর্ব ৪২, পর্ব ৪৩, পর্ব ৪৪, গভীরে: DBMS · Indexing

প্র ১৬ORM না raw SQL? (মাঝারি)

দুর্বল উত্তর: “ORM থাকলে SQL জানার দরকার নেই।” অথবা উল্টো, “ORM ধীর, সবসময় raw SQL.” কেন দুর্বল: দুটিই চরম, আর দুটিই মাপ ছাড়া কথা।

শক্ত উত্তর: এ নিয়ে মত আলাদা, তাই trade-off বলুন। ORM (যেমন Prisma 7.10.1) দেয় type-safe query, migration আর সাধারণ CRUD-এ কম কোড; লুকায় কোন SQL চলছে, তাই N+1 সহজে ঢোকে। Raw SQL দেয় পূর্ণ নিয়ন্ত্রণ (window function, জটিল report), কিন্তু type আর parameter নিজেকে সামলাতে হয়। বাস্তবে মিশ্র: সাধারণ কাজে ORM, ভারী query-তে parameterized raw SQL, আর দুটির ক্ষেত্রেই আসল SQL log-এ দেখা। রিভিশন: পর্ব ৩৮.

প্র ১৭N+1 কী? ২০টি অর্ডারের পাতায় প্রতিটির ক্রেতা আর item আনলে কতটি query? (মাঝারি)

দুর্বল উত্তর: “ORM নিজেই সামলায়।” কেন দুর্বল: loop-এর ভেতরে await থাকলে ORM ঠিক যা বলা হয়েছে তাই করে।

শক্ত উত্তর: N+1 মানে loop-এর ভেতরে query: তালিকার জন্য ১টি, তারপর প্রতিটি সারির সম্পর্কের জন্য আলাদা। দুটি সম্পর্কে ১ + ২ × ২০ = ৪১টি। RTT ৪০ ms হলে শুধু round trip-এই ১.৬ সেকেন্ডের বেশি। সমাধান include বা join, যা ৩টি বা ১টিতে নামায়, আর select-এ শুধু দরকারি কলাম। প্রমাণ: প্রতি request-এ query গোনা, আর test-এ একটি query budget. রিভিশন: পর্ব ৪২.

প্র ১৮শেষ ইলিশটি দুজন একসাথে কিনতে চাইলেন। কী ঘটতে পারে, আর কীভাবে ঠেকাবেন? (কঠিন)

দুর্বল উত্তর: “পুরো কোড একটি transaction-এ মুড়ে দেব।” কেন দুর্বল: PostgreSQL-এর ডিফল্ট Read Committed-এ দুটি transaction-ই একই stock পড়ে দুজনকেই বিক্রি করতে পারে। Transaction একা যথেষ্ট নয়।

শক্ত উত্তর: পড়া, যাচাই, তারপর লেখা: এই ফাঁকেই race condition, ফল overselling. সবচেয়ে সরল সমাধান যাচাই আর লেখা একই statement-এ: UPDATE products SET stock = stock - 1 WHERE id = ? AND stock >= 1, তারপর কয়টি সারি বদলেছে দেখে সিদ্ধান্ত (conditional update). অন্য হাতিয়ার: SELECT … FOR UPDATE, version column, Serializable আর retry, প্রত্যেকটির আলাদা দাম। Transaction ছোট রাখুন, ভেতরে payment gateway ডাকবেন না। অংশ ৭-এ এটি চালিয়ে দেখবেন। রিভিশন: পর্ব ৪৩.

প্র ১৯কোন কলামে index দেবেন? সব কলামে দিলে সমস্যা কী? (মাঝারি)

দুর্বল উত্তর: “সব কলামে index দিলে সব query দ্রুত।” কেন দুর্বল: প্রতিটি index প্রতিটি লেখায় হালনাগাদ হয়, জায়গা নেয়, আর ভুল index planner ব্যবহারই করে না।

শক্ত উত্তর: Index বসে query দেখে, কলাম দেখে নয়। WHERE user_id = ? ORDER BY created_at DESC-এর জন্য (user_id, created_at) composite index; সমতার কলাম আগে, ক্রমের কলাম পরে। মনে রাখুন, PostgreSQL foreign key-তে নিজে index দেয় না। প্রমাণ EXPLAIN ANALYZE: আগে seq scan, পরে index scan. লেখা-ভারী টেবিলে index কম রাখুন। অংশ ৭-এ একটি query plan বদলাতে দেখবেন। রিভিশন: পর্ব ৪২, পর্ব ৩৪.

প্র ২০চলমান অ্যাপে একটি কলামের নাম বদলানোর migration কীভাবে চালাবেন? (কঠিন)

দুর্বল উত্তর: “ALTER TABLE … RENAME COLUMN, তারপর নতুন কোড deploy.” কেন দুর্বল: deploy-এর কয়েক মিনিট পুরোনো আর নতুন কোড একসাথে চলে; পুরোনোটি তখন নেই হয়ে যাওয়া কলাম খোঁজে।

শক্ত উত্তর: Expand and contract: (১) নতুন কলাম যোগ, (২) কোড দুটিতেই লেখে, (৩) পুরোনো সারি ব্যাচে backfill, (৪) কোড নতুনটি পড়ে, (৫) পরের release-এ পুরোনো কলাম মোছা। Migration চলে pipeline-এর আলাদা ধাপে, traffic-এর আগে, prisma migrate deploy দিয়ে, কখনো migrate dev নয়। বড় টেবিলে lock_timeout আর CREATE INDEX CONCURRENTLY. ঝুঁকির আগে backup, আর restore করে দেখা। রিভিশন: পর্ব ৪০, পর্ব ৮১.

প্র ২১Cache কোথায় দেবেন, আর দাম বদলালে cache কীভাবে ঠিক রাখবেন? (কঠিন)

দুর্বল উত্তর: “সব কিছু Redis-এ এক ঘণ্টার জন্য রেখে দিই।” কেন দুর্বল: আগে মাপা হয়নি কোনটি ধীর, আর পুরোনো দাম এক ঘণ্টা দেখানো ব্যবসার ক্ষতি।

শক্ত উত্তর: আগে মাপুন: সস্তা query-তে cache প্রায় কিছুই দেয় না, ভারী পাতায় অনেক দেয়। Cache-aside: আগে Redis, না পেলে database, তারপর TTL দিয়ে রাখা। লেখার পরে নতুন মান SET নয়, key DEL, কারণ দুই লেখার race-এ পুরোনো মান জিতে যেতে পারে; সাথে version guard. জনপ্রিয় key-এর মেয়াদ ফুরালে stampede ঠেকাতে lock বা single-flight. Redis থামলে নিজের deadline দিয়ে database-এ ফিরুন (fail open). টাকা আর stock-এর রায় কখনো cache থেকে নয়। রিভিশন: পর্ব ৪৪.

প্র ২২টাকার পরিমাণ database-এ কোন type-এ রাখবেন? (সহজ)

দুর্বল উত্তর: “FLOAT, কারণ দশমিক আছে।” কেন দুর্বল: binary floating point-এ 0.1 + 0.2 ঠিক 0.3 হয় না, আর হাজার অর্ডারে পয়সার গরমিল জমে।

শক্ত উত্তর: ক্ষুদ্রতম এককে পূর্ণসংখ্যা (minor unit): ৳৭০০ মানে 70000 পয়সা, Int বা BigInt-এ। অথবা DECIMAL, যদি ভাগের হিসাব দরকার হয়। মুদ্রার কলাম আলাদা। অর্ডারে দামের snapshot রাখুন, যাতে পরে পণ্যের দাম বদলালেও পুরোনো রসিদ না বদলায়। Gateway-র webhook-এ পরিমাণ আর মুদ্রা মিলিয়ে তবেই PAID. রিভিশন: পর্ব ৩৯, পর্ব ৪১.

৫ · Queue, real-time, test আর production

রিভিশন: পর্ব ৬০, পর্ব ৬১, পর্ব ৬২, পর্ব ৭২, পর্ব ৭৮, পর্ব ৮১, পর্ব ৮২, পর্ব ৮৩

প্র ২৩অর্ডারের পরে invoice বানানো আর email পাঠানো কোথায় করবেন? (মাঝারি)

দুর্বল উত্তর: “Request-এর ভেতরে sendMail() ডাকি, await ছাড়া, যাতে উত্তর দ্রুত যায়।” কেন দুর্বল: process থামলে কাজটি হারায়; পর্ব ৬২-এর একটি run-এ ৫টি email-এর ১টি পৌঁছেছিল।

শক্ত উত্তর: ধীর বা বাইরের কাজ request-এর বাইরে, একটি job queue-তে, আর আলাদা worker তা চালায়। Delivery সাধারণত at-least-once: worker মাঝপথে মরলে job আবার আসে, তাই কাজটি idempotent হতে হবে (অর্ডার নম্বর দিয়ে “আগে পাঠানো হয়েছে?” দেখা)। সাময়িক ভুলে exponential backoff আর jitter দিয়ে retry; যা সারবে না তা dead letter queue-তে, মানুষের চোখের জন্য। রিভিশন: পর্ব ৬২.

প্র ২৪অর্ডার PAID হলো, আর email-এর job-ও queue-তে দিতে হবে। দুটি কীভাবে একসাথে নিশ্চিত করবেন? (কঠিন)

দুর্বল উত্তর: “আগে database-এ commit, তারপর queue-তে push.” কেন দুর্বল: দুইয়ের মাঝে process মরলে অর্ডার PAID, কিন্তু ক্রেতা কোনো খবর পান না। উল্টো ক্রমে আরও খারাপ: খবর যায়, অর্ডার PAID হয়নি।

শক্ত উত্তর: এটি dual write সমস্যা। Transactional outbox: অবস্থা বদলের একই transaction-এ একটি outbox সারি লিখুন (“অর্ডার ২০৪১-এর রসিদ পাঠাতে হবে”)। একটি relay worker সেই সারি পড়ে queue-তে দেয় বা সরাসরি পাঠায়, সফল হলে চিহ্নিত করে। Database-এর transaction একটিই, তাই অবস্থা আর “পাঠাতে হবে” কখনো আলাদা হয় না। দাম: সামান্য দেরি, আর দুবার পাঠানোর সম্ভাবনা, তাই গ্রাহকের দিকে idempotent. রিভিশন: পর্ব ৪৩, পর্ব ৬৩.

প্র ২৫অর্ডারের অবস্থা live দেখাতে WebSocket, SSE না polling? (মাঝারি)

দুর্বল উত্তর: “Real-time মানেই WebSocket.” কেন দুর্বল: দুই দিকের কথা না থাকলে WebSocket-এর জটিলতা (sticky session, Redis adapter, নিজের auth) অকারণ।

শক্ত উত্তর: প্রশ্ন দিয়ে শুরু: কে কথা বলে, কত ঘন ঘন? অবস্থা দিনে কয়েকবার বদলায়, শুধু server থেকে আসে: SSE, যা সাধারণ HTTP, browser নিজে reconnect করে আর Last-Event-ID পাঠায়। মিনিটে একবার দেখলেই চলে: polling, সবচেয়ে সরল। Chat-এর মতো দুই দিকের ঘন কথা: WebSocket, সংযোগের সময় session যাচাই আর room-এ ঢোকার আগে মালিকানা যাচাইসহ। HTTP/1.1-এ এক host-এ ৬টি সংযোগের সীমা আর proxy-র buffering মাথায় রাখুন। রিভিশন: পর্ব ৬০, পর্ব ৬১.

প্র ২৬বাজারঘরের test কৌশল কী হবে? Coverage কত চাই? (মাঝারি)

দুর্বল উত্তর: “১০০% coverage.” কেন দুর্বল: coverage বলে কোন লাইন চলেছে, ঠিক চলেছে কি না নয়; ১০০% coverage-এর কোডেও সীমানার বাগ থাকে।

শক্ত উত্তর: আকার নয়, ঝুঁকি ধরে। টাকার হিসাব, কুপন আর অবস্থার নিয়মে unit test (Vitest); API-র পথে আসল PostgreSQL-সহ integration test, যেখানে অনুমতি, transaction আর idempotency ধরা পড়ে; আর কয়েকটি মূল যাত্রায় end-to-end test (Playwright): খোঁজা থেকে রসিদ। আচরণ test করুন, implementation-এর খুঁটিনাটি নয়। Coverage মেঝে, ছাদ নয়; mutation testing দুর্বল test ধরিয়ে দেয়। Flaky test সারান বা আলাদা করুন, উপেক্ষা নয়। রিভিশন: পর্ব ৭২, পর্ব ৭৫.

প্র ২৭একটি pull request merge থেকে production পর্যন্ত pipeline বর্ণনা করুন। (মাঝারি)

দুর্বল উত্তর: “Push করলেই server-এ git pull আর restart.” কেন দুর্বল: কোনো যাচাই নেই, migration-এর জায়গা নেই, আর restart-এর মুহূর্তে অনুরোধ ব্যর্থ হয়।

শক্ত উত্তর: প্রতিটি PR-এ একই CI ধাপ: npm ci, lint, type check, unit test, PostgreSQL-সহ integration test, build, E2E. সস্তা check আগে। একটি লাল হলে merge বন্ধ। Merge-এর পরে একটি নির্দিষ্ট build artifact; আলাদা job-এ migration, traffic-এর আগে; তারপর deploy, graceful reload সহ। Rollback মানে আগের build আবার deploy, database পিছিয়ে নয়, তাই migration আগে থেকেই পেছনের দিকে সামঞ্জস্যপূর্ণ। Action পিন করা, secret শুধু দরকারি job-এ। রিভিশন: পর্ব ৮১.

প্র ২৮Environment variable আর secret কীভাবে সামলাবেন? Next.js-এ কী সাবধানতা? (মাঝারি)

দুর্বল উত্তর: “.env ফাইল repo-তে রাখি, দলের সবাই পায়।” কেন দুর্বল: history-তে একবার গেলে secret চিরকাল সেখানে।

শক্ত উত্তর: প্রতিটি পরিবেশের (local, preview, staging, production) আলাদা database আর আলাদা secret, platform-এর settings বা secret manager-এ; repo-তে শুধু একটি উদাহরণ ফাইল, নকল মানসহ। Next.js-এ NEXT_PUBLIC_ দিয়ে শুরু নাম build-এর সময় browser-এর JavaScript-এ লেখা হয়ে যায়, তাই সেখানে কোনো secret নয়। শুরুতেই schema দিয়ে env যাচাই করে fail fast. Secret ফাঁস হলে ফাইল মোছা নয়, key বাতিল করে নতুন বানানো। রিভিশন: পর্ব ৭৮.

প্র ২৯Production-এ কী monitor করবেন, আর কখন রাতে কাউকে ডাকবেন? (কঠিন)

দুর্বল উত্তর: “CPU ৮০% পেরোলে alert.” কেন দুর্বল: CPU একটি কারণ, লক্ষণ নয়; ক্রেতা ভালো থাকলেও রাতে ঘুম ভাঙে, আর alert-এর ক্লান্তি আসে।

শক্ত উত্তর: তিন রকম সংকেত: structured log (প্রতিটিতে request id), metric (golden signals: latency, traffic, error, saturation) আর trace. সাথে error tracking আর বাইরের uptime check. Alert হবে লক্ষণে: যেমন SLO “৯৯.৫% checkout ২ সেকেন্ডে সফল”, আর error budget দ্রুত পুড়লে page. বাকি সব ticket বা dashboard-এ। ব্যবসার metric-ও দেখুন: মিনিটে PAID অর্ডার হঠাৎ শূন্য হলে webhook ভেঙেছে। রিভিশন: পর্ব ৮৩, পর্ব ৩১.

প্র ৩০বাজারঘর কোথায় চালাবেন: VPS, PaaS না serverless? (মাঝারি)

দুর্বল উত্তর: একটি নির্দিষ্ট কোম্পানির নাম, কোনো কারণ ছাড়া। কেন দুর্বল: প্রশ্নটি “কোন কোম্পানি” নয়, “কোন অংশ কোথায় চলবে”।

শক্ত উত্তর: অংশ ধরে ভাবুন: Next.js server, worker, cron, SSE বা WebSocket, PostgreSQL, Redis. VPS-এ নিয়ন্ত্রণ বেশি, দায়িত্বও বেশি (update, backup, reverse proxy, process manager). PaaS-এ অনেক কাজ platform করে, বদলে তার নিয়ম আর দাম। Serverless-এ দীর্ঘ সংযোগ, লম্বা job আর অনেক কপি থেকে database-এ সংযোগের বন্যা সমস্যা, সাথে cold start. দুই জনের দলে managed database আর একটি PaaS বা ঠিকঠাক সাজানো VPS, দুটিই যুক্তিসংগত; খরচ নিজের সংখ্যায় হিসাব করুন। রিভিশন: পর্ব ৮২, পর্ব ৮০.

৬ · Live coding — চারটি backend কাজ, ধাপে ধাপে

রিভিশন: পর্ব ২৪ · Node.js server, পর্ব ২৬, পর্ব ৪৯, পর্ব ৩৩, পর্ব ৩৪, পর্ব ৬৫

Backend-এর live coding-এ সাধারণত কোনো কঠিন algorithm চাওয়া হয় না। চাওয়া হয় একটি ছোট, বাস্তব endpoint, আর দেখা হয় আপনি সীমানার ঘটনা (edge case) ভাবেন কি না। প্রতিটি কাজে একই পাঁচ ধাপ:

  1. প্রশ্ন: কার জন্য, কী সীমা, কোথায় চলবে (এক process না অনেক)? দুই-তিনটি প্রশ্নই যথেষ্ট।
  2. উদাহরণ: তিনটি ইনপুট আর প্রত্যাশিত উত্তর মুখে বলুন, একটি স্বাভাবিক, একটি সীমানার, একটি ভুলের।
  3. সরল সমাধান: আগে চলার মতো কোড, memory-তে store. জোরে বলুন কী বাদ রাখছেন।
  4. Test: সেই তিন উদাহরণ চালিয়ে দেখান। নিচের প্রতিটি ব্লক নিজেই নিজের test.
  5. Production: একাধিক process, স্থায়ী store, header, log আর কী ভাঙতে পারে।

কাজ ১ · Login-এর rate limiter

আগে একটি নকল আলাপ পড়ুন। প্রতিটি ধাপে থেমে নিজের উত্তর আগে ভাবুন।

আলাপ · কাল্পনিক
বক্তা
যা দেখা হচ্ছে
  1. প্রশ্ন। ইচ্ছা করেই অস্পষ্ট। সংখ্যা আর পরিবেশ আপনাকে চেয়ে নিতে হবে।
  2. পরিধি। তিনটি প্রশ্নই নকশা বদলায়। শুধু IP হলে এক অফিসের সবাই আটকান; শুধু account হলে চেষ্টা অনেক account-এ ছড়ায়।
  3. তথ্য। “আপাতত এক process” মানে memory-র Map চলবে, তবে শেষে বাকিটা বলতে হবে।
  4. নকশা। দুটি বিকল্প আর একটির দাম বলা হলো। এটিই শক্ত উত্তরের চিহ্ন। Email ছোট হাতে আনা একটি সূক্ষ্ম কিন্তু জরুরি সিদ্ধান্ত।
  5. কোড আর test. নকল ঘড়ি দিয়ে সময় এগোনো, যাতে test সবসময় একই ফল দেয়। নিচের ব্লকটিই সেই কোড।
  6. চাপ। প্রায় সবসময় আসে: কোন অংশ এক process-এর বাইরে ভাঙে?
  7. Production. Shared store, atomic হালনাগাদ, আর store না থাকলে কী হবে (fail open না fail closed), তিনটিই বলা হলো।
const http = require('node:http');
let now = 0;                                   // fake clock (ms), moved by hand
const CAP = 3, PER_MS = 1 / 1000;              // 3 tokens, refill 1 per second
const buckets = new Map();                     // key -> { tokens, at }; real app: Redis
function take(key) {                           // returns seconds to wait, 0 = allowed
  const b = buckets.get(key) ?? { tokens: CAP, at: now };
  b.tokens = Math.min(CAP, b.tokens + (now - b.at) * PER_MS);
  b.at = now;
  buckets.set(key, b);
  if (b.tokens < 1) return Math.ceil((1 - b.tokens) / PER_MS / 1000);
  b.tokens -= 1;
  return 0;
}
const server = http.createServer((req, res) => {
  const email = new URL(req.url, 'http://x').searchParams.get('email') ?? '';
  const wait = take(req.socket.remoteAddress + '|' + email.toLowerCase());
  if (wait) { res.writeHead(429, { 'Retry-After': String(wait) }); return res.end(); }
  res.end('ok');
});
server.listen(0, '127.0.0.1', async () => {
  const base = `http://127.0.0.1:${server.address().port}/login?email=`;
  const hit = async (e) => { const r = await fetch(base + e, { method: 'POST' });
    return r.status + (r.status === 429 ? '(wait ' + r.headers.get('retry-after') + 's)' : ''); };
  const five = []; for (let i = 0; i < 5; i++) five.push(await hit('rahim@example.com'));
  console.log('t=0.0s rahim x5:', five.join(' '));
  console.log('t=0.0s karim   :', await hit('karim@example.com'));
  now += 1500;
  console.log('t=1.5s rahim x2:', await hit('Rahim@example.com'), await hit('rahim@example.com'));
  server.close();
});

প্রথম লাইন t=0.0s rahim x5: 200 200 200 429(wait 1s) 429(wait 1s): ৩টি token খরচ, তারপর আটকে যায়, আর Retry-After: 1 client-কে বলে কখন আবার আসবে। t=0.0s karim : 200: অন্য account-এর নিজের bucket, একজনের ভুলে আরেকজন আটকান না। শেষ লাইন t=1.5s rahim x2: 200 429(wait 1s): ১.৫ সেকেন্ডে ১.৫টি token ফিরেছে, তাই একটি চলে, পরেরটি আটকায়। দ্বিতীয় অনুরোধে Rahim@ বড় হাতে, তবু একই bucket, কারণ key-তে email ছোট হাতে আনা হয়েছে। Token bucket হঠাৎ ছোট ভিড় সহ্য করে আর গড় হার বেঁধে রাখে; sliding window আরও নিখুঁত, তবে বেশি তথ্য রাখে। একাধিক server-এ Map-এর জায়গায় Redis লাগে, আর পড়া-লেখা একটি atomic ধাপে (পর্ব ৪৯, System Design · Rate limiting).

কাজ ২ · Idempotent অর্ডার তৈরি

প্রশ্ন: “ক্রেতা দুবার ক্লিক করেন, বা network-এ উত্তর হারায়। POST /api/orders যেন একটি অর্ডারই বানায়।” প্রার্থীর প্রশ্ন হওয়া উচিত: key কে বানায় (client), key কার (প্রতি ব্যবহারকারীর আলাদা), একই key-তে ভিন্ন body এলে কী হবে, আর প্রথমটি শেষ হওয়ার আগে দ্বিতীয়টি এলে কী হবে। শেষ প্রশ্নটিই বেশিরভাগ প্রার্থী ভুলে যান।

const http = require('node:http');
const keys = new Map(); let orders = 0;        // "user|key" -> { fp, done, status, body }
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
const server = http.createServer((req, res) => {
  let raw = ''; req.on('data', (c) => (raw += c));
  req.on('end', async () => {
    const key = req.headers['idempotency-key'];
    if (!key) { res.writeHead(400); return res.end('{"code":"KEY_REQUIRED"}'); }
    const slot = 'rahim|' + key, fp = raw, old = keys.get(slot);   // real code: user from session, fp = hash
    if (old && old.fp !== fp) { res.writeHead(422); return res.end('{"code":"KEY_REUSED"}'); }
    if (old && !old.done) { res.writeHead(409); return res.end('{"code":"IN_PROGRESS"}'); }
    if (old) { res.writeHead(old.status, { 'Idempotency-Replayed': 'true' }); return res.end(old.body); }
    keys.set(slot, { fp, done: false });       // claim BEFORE the slow work
    await sleep(30);                           // database + payment session
    const body = JSON.stringify({ id: 'BG-' + (2040 + ++orders), totalPaisa: 70000 });
    keys.set(slot, { fp, done: true, status: 201, body });
    res.writeHead(201); res.end(body);
  });
});
server.listen(0, '127.0.0.1', async () => {
  const url = `http://127.0.0.1:${server.address().port}/api/orders`;
  const post = async (key, body = '{"sku":"ilish-1kg","qty":1}') => {
    const r = await fetch(url, { method: 'POST', body, headers: key ? { 'idempotency-key': key } : {} });
    return `${r.status} ${r.headers.get('idempotency-replayed') ? '(replayed) ' : ''}${await r.text()}`; };
  const [a, b] = await Promise.all([post('k-1'), post('k-1')]);   // double click
  console.log('1+2', [a, b].sort().join(' | '));                  // order may vary
  console.log('3', await post('k-1'));                             // retry after a timeout
  console.log('4', await post('k-1', '{"sku":"ilish-1kg","qty":5}'));
  console.log('5', await post(null), '| orders created:', orders); server.close();
});

1+2 201 {"id":"BG-2041",…} | 409 {"code":"IN_PROGRESS"}: দুটি ক্লিক একসাথে; একটি key দখল করে কাজ শুরু করে, অন্যটি দেখে কাজ চলছে, তাই 409. দখল হয় ধীর কাজের আগে; পরে হলে দুটিই ঢুকে যেত। 3 201 (replayed) {"id":"BG-2041",…}: পরে retry-তে জমানো উত্তর, নতুন অর্ডার নয়। 4 422 {"code":"KEY_REUSED"}: একই key, ভিন্ন body (qty ১ থেকে ৫), যা প্রায়ই client-এর বাগ, তাই নীরবে পুরোনো উত্তর দেওয়া ঠিক হতো না। 5 400 … | orders created: 1: key ছাড়া অনুরোধ ফেরত, আর মোট অর্ডার একটিই। Production-এ store হবে database-এর একটি টেবিল, (user_id, key)-এ unique constraint, যাতে দুই process-এর দখলের race-ও database মেটায়; key-এর মেয়াদ, যেমন ২৪ ঘণ্টা; আর fingerprint হবে body-র hash (পর্ব ৩৩)।

কাজ ৩ · Cursor দিয়ে পণ্যের তালিকা

প্রশ্ন: “নতুন পণ্য আগে, পাতায় ৩টি করে। মাঝে নতুন পণ্য এলেও কিছু যেন দুবার না আসে বা হারিয়ে না যায়।” সীমানার ঘটনা: শেষ পাতা, সমান সময়ের দুটি পণ্য, আর বিশাল limit.

const http = require('node:http');
// products, newest first by (created, id); created is a fake timestamp
const rows = [1, 2, 3, 4, 5, 6, 7].map((id) => ({ id, created: 100 + id * 10 }));
const enc = (r) => Buffer.from(JSON.stringify([r.created, r.id])).toString('base64url');
const dec = (c) => JSON.parse(Buffer.from(c, 'base64url').toString());
const sorted = () => [...rows].sort((a, b) => b.created - a.created || b.id - a.id);  // id breaks ties
const server = http.createServer((req, res) => {
  const q = new URL(req.url, 'http://x').searchParams;
  const limit = Number(q.get('limit') ?? 3);
  if (!Number.isInteger(limit) || limit < 1 || limit > 50) { res.writeHead(400); return res.end('{"code":"BAD_LIMIT"}'); }
  let list = sorted();
  if (q.get('cursor')) { const [c, i] = dec(q.get('cursor'));        // WHERE (created, id) < (c, i)
    list = list.filter((r) => r.created < c || (r.created === c && r.id < i)); }
  const page = list.slice(0, limit + 1);                             // limit + 1, no COUNT(*)
  const hasMore = page.length > limit; if (hasMore) page.pop();
  res.end(JSON.stringify({ items: page.map((r) => r.id), nextCursor: hasMore ? enc(page.at(-1)) : null }));
});
server.listen(0, '127.0.0.1', async () => {
  const get = async (qs) => { const r = await fetch(`http://127.0.0.1:${server.address().port}/products?${qs}`);
    return r.status === 200 ? r.json() : r.status + ' ' + await r.text(); };
  const p1 = await get('limit=3');
  console.log('page 1:', JSON.stringify(p1));
  rows.push({ id: 8, created: 190 });                                // a new product arrives
  const p2 = await get('limit=3&cursor=' + p1.nextCursor);
  console.log('page 2:', JSON.stringify(p2.items), '| OFFSET 3 now gives:', JSON.stringify(sorted().slice(3, 6).map((r) => r.id)));
  console.log('page 3:', JSON.stringify(await get('limit=3&cursor=' + p2.nextCursor)));
  console.log('limit=1000:', await get('limit=1000'));
  server.close();
});

page 1: {"items":[7,6,5],"nextCursor":"WzE1MCw1XQ"}: cursor-টি আসলে [150,5]-এর base64url, কিন্তু client-এর কাছে opaque; ভেতরের গড়ন পরে বদলানো যায়। তারপর নতুন পণ্য ৮ এল। page 2: [4,3,2] | OFFSET 3 now gives: [5,4,3]: cursor শেষ দেখা সারির পর থেকে শুরু করে, তাই কিছু দুবার আসে না; OFFSET 3 হলে পণ্য ৫ দ্বিতীয়বার দেখা যেত, কারণ সব সারি এক ঘর সরে গেছে। page 3: {"items":[1],"nextCursor":null}: limit + 1 আনা হয়েছিল, বাড়তিটি না থাকায় null, কোনো COUNT(*) ছাড়াই। limit=1000: 400 {"code":"BAD_LIMIT"}: ওপরের সীমা না থাকলে একটি অনুরোধেই পুরো টেবিল। SQL-এ এর রূপ WHERE (created_at, id) < ($1, $2) ORDER BY created_at DESC, id DESC LIMIT $3, সাথে (created_at, id)-এর index (পর্ব ৩৪)।

কাজ ৪ · Payment webhook যাচাই

প্রশ্ন: “Gateway x-signature: t=…,v1=… header-এ HMAC-SHA256 সই পাঠায়, t.body-এর ওপর। একটি endpoint লিখুন যা শুধু আসল আর নতুন খবরই নেয়।” এখানে চারটি জিনিস খোঁজা হয়: raw body, নির্দিষ্ট-সময়ের তুলনা, সময়সীমা, আর duplicate. Header-এর নাম আর গড়ন এখানে আমাদের বানানো; আসল gateway-র docs দেখে মেলাতে হয়।

const http = require('node:http'), { createHmac, timingSafeEqual } = require('node:crypto');
const SECRET = 'example-secret', NOW = 1793640000, seen = new Set();  // fake secret, fixed clock
const sign = (t, raw) => createHmac('sha256', SECRET).update(`${t}.${raw}`).digest('hex');
function verify(header = '', raw) {
  const { t, v1 } = Object.fromEntries(header.split(',').map((kv) => kv.split('=')));
  if (!t || Math.abs(NOW - Number(t)) > 300) return 'too old';
  const exp = Buffer.from(sign(t, raw), 'hex'), got = Buffer.from(v1 ?? '', 'hex');
  return got.length === exp.length && timingSafeEqual(got, exp) ? null : 'bad signature';
}
const server = http.createServer((req, res) => {
  const chunks = []; req.on('data', (c) => chunks.push(c));
  req.on('end', () => {
    const raw = Buffer.concat(chunks).toString('utf8');      // the exact bytes that were signed
    const bad = verify(req.headers['x-signature'], raw);
    if (bad) { res.writeHead(401); return res.end(bad); }
    const ev = JSON.parse(raw);                              // parse only after the check
    if (seen.has(ev.id)) return res.end('duplicate ' + ev.id);
    seen.add(ev.id); res.end('processed ' + ev.id);            // real code: unique column
  });
});
server.listen(0, '127.0.0.1', async () => {
  const send = async (raw, t = NOW, sig = sign(t, raw)) => {
    const r = await fetch(`http://127.0.0.1:${server.address().port}/webhooks/pay`,
      { method: 'POST', body: raw, headers: { 'x-signature': `t=${t},v1=${sig}` } });
    return r.status + ' ' + await r.text(); };
  const raw = '{"id":"evt_31","ref":"BG-2041","amountPaisa":70000}';
  console.log('1', await send(raw), '| 2', await send(raw));                    // 2 = gateway retry
  console.log('3', await send(raw.replace('70000', '700'), NOW, sign(NOW, raw)));  // body changed
  console.log('4', await send(raw, NOW - 3600)); server.close();                  // an hour old
});

1 200 processed evt_31 | 2 200 duplicate evt_31: gateway একই খবর আবার পাঠায় (তারা সাধারণত 2xx না পাওয়া পর্যন্ত চেষ্টা করে), আর আমরা 200 দিই, কিন্তু কিছুই দুবার করি না; বাস্তবে event id-র একটি unique column. 3 401 bad signature: পরিমাণ 70000 থেকে 700 করা হয়েছে, পুরোনো সই আর মেলে না। 4 401 too old: এক ঘণ্টা আগের একটি আসল বার্তা আবার পাঠানো, অর্থাৎ replay; ৩০০ সেকেন্ডের সীমা তা থামায়। তিনটি সূক্ষ্ম বিষয় বলুন। এক, সই যাচাই raw bytes-এর ওপর, JSON parse-এর আগে; parse করে আবার JSON.stringify করলে ফাঁকা জায়গা বা key-এর ক্রম বদলে সই মেলে না। দুই, timingSafeEqual, যাতে তুলনার সময় দেখে সই অনুমান করা না যায়; তার আগে দৈর্ঘ্য মেলানো, নইলে সে নিজেই error দেয়। তিন, সই মিললেও শেষ সিদ্ধান্তের আগে পরিমাণ আর মুদ্রা মেলানো আর gateway-র validation API-কে জিজ্ঞেস করা (পর্ব ৬৫)।

৭ · Debugging round — চারটি backend বাগ

রিভিশন: পর্ব ৪৩, পর্ব ২৮, পর্ব ৪৮, পর্ব ৪২, পর্ব ৩১

Debugging round-এ সাধারণত একটি ভাঙা কোড, একটি log বা একটি অভিযোগ দেওয়া হয়: “মাঝে মাঝে stock ঋণাত্মক”, “পাতাটি ধীর”। দেখা হয় আপনি অনুমানে কোড বদলান, না প্রমাণ খোঁজেন। পাঁচ ধাপ মনে রাখুন, ডাক্তারের মতো: উপসর্গ লেখা, নিজে ঘটানো (reproduce), মাপ বা log দেখা, একটি অনুমান যাচাই, তারপর সারানো আর একটি regression test, যাতে বাগটি আর না ফেরে।

বাগ ১ · “শেষ জারটি পাঁচজন কিনেছেন”

অভিযোগ: ঈদের সেলে stock ১ ছিল, অর্ডার এল পাঁচটি, আর stock দেখাচ্ছে 0, যেন সব ঠিক। প্রথম সূত্র: ভুলটি শুধু ভিড়ে হয়, একা test করলে হয় না। ভিড়ে হওয়া বাগ মানেই একসাথে চলা কাজের মধ্যে ক্রমের সমস্যা। দুই ক্রেতার ক্রমটি ধাপে ধাপে দেখুন।

Database আর দুই request
ধাপ
A দেখে
B দেখে
stock (DB)
অর্ডার
  1. A পড়ে। Request A stock পড়ল, 1. যাচাই পাস।
  2. B পড়ে। A লেখার আগেই B-ও পড়ল, 1. দুজনের হাতেই এখন একই পুরোনো ছবি।
  3. A লেখে। A লিখল 1 - 1 = 0, অর্ডার একটি। এ পর্যন্ত ঠিক।
  4. B লেখে। B-ও নিজের ছবি থেকে 1 - 1 = 0 লিখল। অর্ডার দুটি, stock 0. লেখাটি আগের লেখাকে মুছে দিয়েছে, তাই ভুলটি সংখ্যায় দেখা যায় না, দেখা যায় শুধু অর্ডারের গোনায়।
  5. সমাধান। যাচাই আর লেখা এক statement-এ। Database row lock দিয়ে একজনকেই জিততে দেয়; B দেখে 0 সারি বদলেছে, আর “sold out” বলে।

নিচের শিক্ষণ মডেলে প্রতিটি await মানে database-এ একটি যাত্রা। পাঁচজন ক্রেতা একসাথে, stock ১.

// Teaching model: a table in memory. Each await = one trip to the database.
const trip = () => new Promise((r) => setImmediate(r));
function makeDb(stock) {
  const row = { stock }; let orders = 0;
  return {
    async readStock() { await trip(); return row.stock; },
    async writeStock(n) { await trip(); row.stock = n; },
    async decrementIf(n) { await trip();                 // UPDATE ... SET stock = stock - n
      if (row.stock < n) return 0;                       //   WHERE id = ? AND stock >= n
      row.stock -= n; return 1; },                       // returns the row count
    async addOrder() { await trip(); orders++; },
    report: () => `orders ${orders}, stock ${row.stock}`,
  };
}
async function buggy(db) {                               // read, check, then write
  const s = await db.readStock();
  if (s < 1) return 'sold out';
  await db.writeStock(s - 1); await db.addOrder(); return 'ok';
}
async function fixed(db) {                               // check and write in ONE statement
  if ((await db.decrementIf(1)) === 0) return 'sold out';
  await db.addOrder(); return 'ok';
}
(async () => {
  for (const fn of [buggy, fixed]) {
    const db = makeDb(1);                                // one ilish left, five buyers at once
    const r = await Promise.all([1, 2, 3, 4, 5].map(() => fn(db)));
    console.log(fn.name.padEnd(6), r.join(' / ').padEnd(46), db.report());
  }
})();

buggy ok / ok / ok / ok / ok … orders 5, stock 0: পাঁচজনই “ok” পেলেন, অথচ stock 0 দেখে কিছু বোঝা যায় না। fixed ok / sold out / sold out / sold out / sold out orders 1, stock 0: শর্তসহ একটি হালনাগাদ, একজন জেতেন। ইন্টারভিউতে “কীভাবে খুঁজে পেলেন” বলুন: অর্ডারের সংখ্যা আর stock-এর হিসাব মেলে না (একটি reconciliation query), log-এ একই পণ্যের অনুরোধ একই মিলিসেকেন্ডে, আর একটি test যা একসাথে অনেক অনুরোধ পাঠায়। সারানোর পরে database-এ CHECK (stock >= 0) একটি শেষ বেড়া। পর্ব ৪৩-এর আসল PostgreSQL run-এ transaction ছাড়া কোড একবারে ১৩ থেকে ২০টি অর্ডার নিয়েছিল (পর্ব ৪৩)।

বাগ ২ · Error-এর সাথে stack trace বাইরে যাচ্ছে

অভিযোগ: একজন ক্রেতা screenshot পাঠালেন, signup-এ পর্দায় database-এর বার্তা আর অনেক লাইনের কোডের path. খোঁজার পথ: browser-এর DevTools-এ response দেখা, তারপর error handler খোঁজা। সাধারণ কারণ, development-এর handler production-এ রয়ে গেছে।

const http = require('node:http');
const { randomUUID } = require('node:crypto');
const logs = [];                                         // stands in for the server log
function saveUser() {                                    // pretend the database said this
  throw new Error('duplicate key value violates unique constraint "users_email_key"');
}
const leaky = (err, res) => { res.writeHead(500); res.end(err.stack); };
const safe = (err, res) => {
  const traceId = randomUUID().slice(0, 8);
  logs.push({ traceId, msg: err.message, frames: err.stack.split('\n').length - 1 });
  res.writeHead(500, { 'Content-Type': 'application/problem+json' });
  res.end(JSON.stringify({ type: 'about:blank', title: 'Something went wrong', status: 500, traceId }));
};
const server = http.createServer((req, res) => {
  try { saveUser(); res.end('ok'); }
  catch (err) { (req.url === '/leaky' ? leaky : safe)(err, res); }
});
server.listen(0, '127.0.0.1', async () => {
  const base = `http://127.0.0.1:${server.address().port}`;
  for (const path of ['/leaky', '/safe']) {
    const body = await (await fetch(base + path, { method: 'POST' })).text();
    const lines = body.split('\n');
    console.log(path, 'client sees:', lines[0].slice(0, 70));
    console.log('       + stack lines sent to client:', lines.length - 1);
  }
  const l = logs[0];
  console.log('server log:', l.msg.slice(0, 40) + '...', 'frames', l.frames > 0, 'trace', l.traceId.length === 8);
  server.close();
});

/leaky client sees: Error: duplicate key value violates unique constraint "users_email_key আর + stack lines sent to client: 5: client পেলেন database-এর বার্তা, constraint-এর নাম আর পাঁচ লাইনের stack, যেখানে server-এর ফাইলের path থাকে (এখানে ইচ্ছা করেই ছাপানো হয়নি)। /safe client sees: {"type":"about:blank","title":"Something went wrong","status":500,… আর 0: শুধু নিরাপদ বার্তা আর একটি trace id. শেষ লাইন server log: … frames true trace true: বিবরণ হারায়নি, গেছে log-এ, একই trace id সহ; ক্রেতা id-টি জানালে আপনি ঘটনাটি খুঁজে পান। সারানো মানে একটি central error handler (পর্ব ২৮), আর production-এ development-এর handler বন্ধ। Duplicate email আসলে একটি প্রত্যাশিত ভুল, 500 নয়; তবে signup-এর উত্তর এমন রাখুন যাতে email আছে কি না বোঝা না যায় (পর্ব ৪৯)। Regression test: production mode-এ একটি ভুল ঘটিয়ে দেখা, response-এ stack-এর লাইন আছে কি না।

বাগ ৩ · অন্যের অর্ডার খুলে যায়

অভিযোগ: একজন ক্রেতা বললেন, URL-এর নম্বর একটু বদলাতেই অন্য কারও ঠিকানা দেখা গেল। নিজে ঘটাতে দুটি account লাগে। কারণ খুঁজুন query-তে: findById-এ শুধু id, মালিক নেই।

const http = require('node:http');
const sessions = { 'sid-rahim': 'u1', 'sid-karim': 'u2' };   // cookie value -> user id
const orders = [
  { id: 2041, userId: 'u1', address: 'Rajshahi', totalPaisa: 70000 },
  { id: 2042, userId: 'u2', address: 'Khulna', totalPaisa: 125000 },
];
const findById = (id) => orders.find((o) => o.id === id);                    // bug
const findOwned = (id, userId) => orders.find((o) => o.id === id && o.userId === userId); // fix
const server = http.createServer((req, res) => {
  const sid = (req.headers.cookie ?? '').replace('sid=', '');
  const userId = sessions[sid];
  if (!userId) { res.writeHead(401); return res.end('{"code":"LOGIN_REQUIRED"}'); }
  const [, mode, , id] = req.url.split('/');                   // /buggy/orders/2042
  const order = mode === 'buggy' ? findById(Number(id)) : findOwned(Number(id), userId);
  if (!order) { res.writeHead(404); return res.end('{"code":"NOT_FOUND"}'); }  // 404, not 403
  res.end(JSON.stringify(order));
});
server.listen(0, '127.0.0.1', async () => {
  const get = async (path, sid) => {
    const r = await fetch(`http://127.0.0.1:${server.address().port}${path}`, { headers: sid ? { cookie: 'sid=' + sid } : {} });
    return `${r.status} ${await r.text()}`; };
  console.log('no login      :', await get('/fixed/orders/2041'));
  console.log('rahim, own    :', await get('/fixed/orders/2041', 'sid-rahim'));
  console.log('rahim -> 2042 buggy:', await get('/buggy/orders/2042', 'sid-rahim'));
  console.log('rahim -> 2042 fixed:', await get('/fixed/orders/2042', 'sid-rahim'));
  console.log('rahim -> 9999 fixed:', await get('/fixed/orders/9999', 'sid-rahim'));
  server.close();
});

no login : 401: পরিচয়ই নেই। rahim, own : 200: নিজের অর্ডার, ঠিক। rahim -> 2042 buggy: 200 {"id":2042,"userId":"u2","address":"Khulna",…}: login করা রহিম করিমের ঠিকানা পেলেন। এটিই IDOR. rahim -> 2042 fixed: 404 আর rahim -> 9999 fixed: 404: অন্যের অর্ডার আর না-থাকা অর্ডারে হুবহু একই উত্তর, তাই কোন নম্বরে অর্ডার আছে তাও বোঝা যায় না। সারানোর সাথে তিনটি কাজ বলুন: সব route-এ একই নীতি (একটি policy function বা repository-স্তরের সীমা), দুই ক্রেতার একটি test matrix, আর log খুঁজে দেখা অতীতে কেউ এভাবে পড়েছেন কি না; ব্যক্তিগত তথ্য গিয়ে থাকলে সেটি একটি incident (পর্ব ৪৮)।

বাগ ৪ · “আমার অর্ডার” পাতা ধীর

অভিযোগ: অর্ডারের ইতিহাসের পাতা মাস দুয়েক ধরে ধীর হচ্ছে। “ধীর হচ্ছে” মানে ডেটা বাড়ছে আর কাজও তার সাথে বাড়ছে। আগে slow query log বা request-এর trace দেখে নিশ্চিত হোন সময় কোথায় যাচ্ছে: অনেক query (N+1, পর্ব ৪২), না একটি ধীর query. এখানে দ্বিতীয়টি। নিচে ২০,০০০ অর্ডারের একটি SQLite টেবিলে query plan দেখুন, index-এর আগে আর পরে।

-- Before: the query behind the "my orders" page
EXPLAIN QUERY PLAN
SELECT id, status, created_at FROM orders
WHERE user_id = 42 ORDER BY created_at DESC LIMIT 20;

-- Fix: equality column first, then the sort column
CREATE INDEX orders_user_created ON orders (user_id, created_at);

EXPLAIN QUERY PLAN
SELECT id, status, created_at FROM orders
WHERE user_id = 42 ORDER BY created_at DESC LIMIT 20;

SELECT COUNT(*) AS rows_in_table,
       SUM(user_id = 42) AS rows_for_user_42
FROM orders;

প্রথম plan SCAN orders আর USE TEMP B-TREE FOR ORDER BY: পুরো ২০,০০০ সারি পড়া, তারপর আলাদা করে সাজানো, অথচ শেষ ফলে ক্রেতা ৪২-এর সারি মাত্র 10টি। Index-এর পরে SEARCH orders USING INDEX orders_user_created (user_id=?): সরাসরি ওই ক্রেতার সারিতে, আর সাজানোর ধাপ নেই, কারণ index-এর ভেতরে সারিগুলো আগে থেকেই created_at ক্রমে। Scan-এর খরচ টেবিলের আকারের সাথে বাড়ে, তাই পাতাটি মাসে মাসে ধীর হচ্ছিল। এটি SQLite-এর plan; PostgreSQL-এ EXPLAIN ANALYZE একই ছবি দেয়, আসল সময়সহ। পর্ব ৪২-এর একটি run-এ সঠিক index একটি query-কে ২৬ ms থেকে ০.০৩৩ ms-এ নামিয়েছিল। চালু টেবিলে PostgreSQL-এ CREATE INDEX CONCURRENTLY (পর্ব ৪০)।

৮ · System design — বাজারঘরের checkout আর অর্ডারের notification

রিভিশন: পর্ব ৮৭ · একটি অ্যাপ বড় করা, পর্ব ৮৮, তত্ত্ব: System Design · নকশার কাঠামো, Estimation, Notification system

প্রশ্ন: “বাজারঘরের দৈনিক ব্যবহারকারী ১ লাখ। Checkout আর অর্ডারের notification (email, SMS, অ্যাপের ভেতরে) নকশা করুন।” এখানে একটি আদর্শ উত্তর, যেভাবে ৪৫ মিনিটে মুখে বলা যায়। কাঠামোটি সব নকশার প্রশ্নে খাটে: চাহিদা, সংখ্যা, API আর ডেটা, ছবি, গভীরে একটি অংশ, তারপর trade-off.

ধাপ ১ · চাহিদা (৫ মিনিট)

  • কাজের চাহিদা: কার্ট থেকে অর্ডার; stock আটকে রাখা; gateway-র hosted পাতায় টাকা; পরিশোধ, পাঠানো আর পৌঁছানোর প্রতিটি ধাপে ক্রেতাকে খবর; অ্যাপে অবস্থা live.
  • অন্য চাহিদা: এক পণ্য দুজনকে নয়, এক অর্ডারে দুবার টাকা নয়, খবর হারাবে না (দেরি চলবে, দুবার এড়াতে হবে); checkout-এর p95 ধরা যাক ১ সেকেন্ডের নিচে।
  • বাইরে রাখা: search, recommendation, বিক্রেতার dashboard. বলে নিন, যাতে সময় নষ্ট না হয়।

ধাপ ২ · সংখ্যা (৫ মিনিট)

পর্ব ৮৭-এর একই অনুমান নিন: জনপ্রতি দিনে ৩০টি অনুরোধ, সন্ধ্যার peak গড়ের ৫ গুণ, ঈদের সেলে ২০ গুণ, ১০০ জনে দিনে ৪টি অর্ডার। প্রতিটি অনুমান মুখে বলুন; ইন্টারভিউয়ার বদলাতে চাইলে বদলান।

// Teaching model. Every number is an assumption: replace it with your own measurements.
const DAU = 100000, REQ_PER_USER = 30;          // same assumptions as the scaling lesson
const PEAK = 5, EID = 20;                        // evening peak, Eid-sale peak (x daily average)
const ORDERS_PER_USER = 0.04;                    // 4 orders per 100 users per day
const SQL_PER_CHECKOUT = 8, MS_PER_SQL = 4;      // stock, order, items, payment row, outbox...
const MSGS_PER_ORDER = 3 * 3;                    // 3 status changes x (email, SMS, in-app)
const f = (n) => (n >= 100 ? Math.round(n) : +n.toFixed(2));
const perSec = (perDay, factor = 1) => (perDay / 86400) * factor;

const req = DAU * REQ_PER_USER, orders = DAU * ORDERS_PER_USER;
console.log('requests/s  avg', f(perSec(req)), '| peak', f(perSec(req, PEAK)), '| Eid', f(perSec(req, EID)));
console.log('orders/day', orders, '| orders/s at Eid peak', f(perSec(orders, EID)));
const sql = perSec(orders, EID) * SQL_PER_CHECKOUT;
console.log('checkout SQL/s at Eid', f(sql), '| busy connections L = λW:', f(sql * MS_PER_SQL / 1000));
const msgs = orders * MSGS_PER_ORDER;
console.log('notifications/day', msgs, '| per s at Eid', f(perSec(msgs, EID)));

// The real limit: one hot product row. Its lock is held for the whole transaction.
for (const txMs of [20, 5]) {
  const maxPerSec = 1000 / txMs, want = 500 / 10;  // 500 buyers in 10 s for one item
  console.log(`hot row, tx ${txMs} ms: max ${maxPerSec}/s, demand ${want}/s, load ${f(want / maxPerSec * 100)}%`);
}
console.log('order data/year', f(orders * 365 * 4 / 1024 / 1024), 'GB at 4 KB per order');

requests/s avg 34.72 | peak 174 | Eid 694: পর্ব ৮৭-এর সংখ্যাই। কিন্তু checkout-এর লেখা খুব ছোট: orders/s at Eid peak 0.93, checkout SQL/s at Eid 7.41, আর Little-এর সূত্রে busy connections L = λW: 0.03. notifications/day 36000 | per s at Eid 8.33: একটি worker-ই যথেষ্ট, তবে বাইরের SMS আর email সেবার নিজের হারের সীমা মানতে হয়। আসল সীমা এক সারিতে: hot row, tx 20 ms: max 50/s, demand 50/s, load 100%, অর্থাৎ ১০ সেকেন্ডে ৫০০ জন একটি পণ্য চাইলে সারির সামনে লাইন জমে; tx 5 ms … load 25%. শেষ লাইন 5.57 GB: এক বছরের অর্ডার একটি সাধারণ database-এ সহজেই আঁটে। শিক্ষা: ১ লাখ শুনে sharding বা microservice নয়। এই আকারে মূল ঝুঁকি সঠিকতা আর একটি জনপ্রিয় সারি; পড়ার ভার (৬৯৪ req/s) সামলায় cache, CDN আর read replica (পর্ব ৮৭)।

ধাপ ৩ · API, ডেটা আর ছবি (১০ মিনিট)

তিনটি endpoint: POST /api/orders (Idempotency-Key সহ, উত্তরে gateway-র URL), POST /webhooks/payment (সই করা), আর GET /api/orders/:id/events (SSE, শুধু মালিকের জন্য)। মূল টেবিল: products(stock), orders(status, totalPaisa), order_items(price snapshot), payments(event_id unique), outbox আর notifications(order_id, channel, kind unique).

বাজারঘরের checkout আর notification: এক নজরে

Checkout, payment webhook, outbox and notification workers (a model answer, not a deployed system)

Checkout আর notification-এর architecture ক্রেতার browsercheckout, SSE Payment gatewayhosted পাতা, webhook Load balancer → ৩টি app server (Next.js) যাচাই, Idempotency-Key, সই যাচাই gateway-কে ডাকা transaction-এর বাইরে PostgreSQL (primary) এক transaction-এ: stock − n, order, items, outbox সারি Read replica তালিকা, report checkout নয় Outbox relayসারি পড়ে queue-তে Redis queueretry, dead letter Notification worker email · SMS · অ্যাপে খবর, দুবার নয়
ছবি ৯৩.২ — টাকা আর stock-এর সত্য থাকে একটি database-এর একটি transaction-এ; বাকি সব (খবর, live অবস্থা) তার পেছনে, আবার চেষ্টা করা যায় এমনভাবে।

ধাপ ৪ · একটি অর্ডারের যাত্রা, গভীরে (১৫ মিনিট)

অর্ডার BG-2041 · কাল্পনিক
ধাপ
order
stock
outbox
  1. Checkout. দাম কখনো client-এর কথায় নয়। Key দিয়ে দুবার ক্লিক বা retry-তে একটি অর্ডারই হয় (অংশ ৬-এর কাজ ২)।
  2. Transaction. শর্তসহ হালনাগাদে stock আটকে যায়, আর অর্ডার লেখা হয় একসাথে। Transaction কয়েক মিলিসেকেন্ডের, কারণ ভেতরে কোনো network-এর ডাক নেই।
  3. Gateway. Payment session বানানো বাইরের ডাক, তাই transaction শেষ হওয়ার পরে। Gateway ব্যর্থ হলে অর্ডার PENDING থাকে, আর ধাপ ৭ stock ফেরত দেয়।
  4. Webhook. অংশ ৬-এর কাজ ৪. WHERE status = 'PENDING_PAYMENT' থাকায় দেরিতে আসা বা উল্টো ক্রমের খবর অবস্থা নষ্ট করতে পারে না। অবস্থা বদল আর outbox সারি এক transaction-এ।
  5. Outbox থেকে queue. Relay সারিটি পড়ে job দেয়, তারপর চিহ্নিত করে। মাঝে মরলে job দুবার যেতে পারে, তাই পরের ধাপ idempotent.
  6. Worker. প্রতিটি খবরের আগে (order_id, channel, kind)-এ unique সারি: দ্বিতীয়বার এলে কিছু পাঠায় না। প্রতিটি channel আলাদা job, যাতে SMS ধীর হলে email আটকে না যায়।
  7. মেয়াদ। টাকা না এলে stock চিরকাল আটকে থাকবে না। একই শর্তসহ হালনাগাদ, যাতে webhook আর cron একসাথে এলে একটিই জেতে।

ধাপ ৫ · Trade-off আর বড় হলে কী (১০ মিনিট)

সিদ্ধান্তকেনদাম বা ঝুঁকি
Checkout-এ stock আটকানো, পেমেন্টে নয়যিনি টাকা দিলেন তিনি পণ্য পাবেনঅপরিশোধিত অর্ডার stock ধরে রাখে; ৩০ মিনিটের মেয়াদ লাগে
একটি PostgreSQL primary, microservice নয়টাকা আর stock এক transaction-এ থাকে; লেখার ভার খুব কমPrimary একটি SPOF; backup, replica আর failover-এর পরিকল্পনা লাগে
Outbox আর queueখবর হারায় না, checkout বাইরের সেবার গতিতে আটকায় নাকয়েক সেকেন্ড দেরি; দুবার পাঠানোর সম্ভাবনা, তাই unique সারি
SSE, WebSocket নয়খবর শুধু server থেকে, দিনে কয়েকবারপ্রতিটি খোলা পাতা একটি সংযোগ ধরে রাখে; proxy-র buffering বন্ধ রাখতে হয়
Hot product-এ শুধু ছোট transactionএক সারির lock-এ সেকেন্ডে কতটি, তা transaction-এর সময়ই ঠিক করেFlash sale বড় হলে আলাদা পথ লাগতে পারে: queue-তে সারি বা stock-কে কয়েক ভাগে ভাগ

শেষে বলুন কী মাপবেন: checkout-এর p95, মিনিটে PAID অর্ডার, queue-এর গভীরতা আর সবচেয়ে পুরোনো job-এর বয়স, dead letter-এর সংখ্যা, আর webhook-এর সই-ব্যর্থতা। ১০ লাখ ব্যবহারকারীতে কী বদলাবে, তার পূর্ণ গল্প পর্ব ৮৭-এ; বড় notification ব্যবস্থার নকশা System Design-এর notification case study-তে।

প্র ৩১ইন্টারভিউয়ার বললেন, “Order আর notification আলাদা microservice করুন।” কী বলবেন? (কঠিন)

দুর্বল উত্তর: সাথে সাথে রাজি হয়ে দুটি service আঁকা, অথবা “microservice খারাপ” বলে থামানো।

শক্ত উত্তর: “করা যায়। কী পাব: notification দল আলাদা deploy করতে পারবে, আর SMS-এর ভিড় checkout-কে ছোঁবে না। কী দেব: network-এর ডাক, আলাদা database, আর service পেরোনো transaction নেই, তাই outbox বা event-এর ওপর নির্ভরতা বাড়ে। এই সংখ্যায় (সেকেন্ডে ১০টির নিচে খবর) ভারের জন্য আলাদা করার দরকার নেই। যেহেতু আমরা আগেই queue দিয়ে আলাদা worker রেখেছি, দল বা ভার সত্যিই আলাদা হলে সীমাটি পরিষ্কার, পরে কাটা সহজ।” এটি পর্ব ৮৭-এর modular monolith-এর যুক্তি। রিভিশন: পর্ব ০৪, পর্ব ৮৭.

৯ · আচরণগত প্রশ্ন, STAR আর নিজের প্রকল্প নিয়ে কথা

রিভিশন: পর্ব ৮৮ · ক্যাপস্টোন, পর্ব ৮৬, পাশের ট্র্যাক: Software engineering ইন্টারভিউ

আচরণগত প্রশ্নে STAR কাঠামো কাজে দেয়: Situation (পরিস্থিতি), Task (আপনার দায়িত্ব), Action (আপনি নিজে কী করলেন), Result (ফল, সম্ভব হলে সংখ্যায়)। শেষে একটি শিক্ষা যোগ করুন। দুই মিনিটের মধ্যে রাখুন। চাকরির অভিজ্ঞতা না থাকলে নিজের প্রকল্পই আপনার ঘটনা, আর তা সম্পূর্ণ বৈধ।

সততার নিয়ম: নিচের উত্তরগুলো এই কোর্সের ক্যাপস্টোনের (পর্ব ৮৮) reference app-এ সত্যিই যা ঘটেছে, তা দিয়ে লেখা, কাঠামো দেখাতে। আপনি নিজের প্রকল্পে যা করেছেন, শুধু তাই বলুন। অন্যের, internet-এর বা AI-এর বানানো গল্প নিজের বলে বলবেন না; ফলো-আপের দ্বিতীয় প্রশ্নেই তা ভেঙে যায়, আর সততা নিয়ে প্রশ্ন ওঠে।

প্র ৩২“একটি কঠিন bug-এর কথা বলুন।” (মাঝারি)

দুর্বল উত্তর: “একবার একটা বাগ ছিল, অনেক খুঁজে ঠিক করেছি।” কেন দুর্বল: কী, কীভাবে, কী শিখলেন, কিছুই নেই।

শক্ত উত্তর: S: “আমার ক্যাপস্টোনে seed চালাতেই বাংলা পণ্যের নাম ঢুকছিল না, error দিচ্ছিল।” T: “কারণ খোঁজা আমার কাজ, কারণ seed ছাড়া কোনো test চলছিল না।” A: “আন্দাজে কোড বদলানোর আগে error code পড়লাম, তারপর database-এর encoding দেখলাম। Cluster বানানো হয়েছিল একটি Windows ডিফল্ট encoding-এ, UTF8-এ নয়। UTF8-এ নতুন করে বানালাম, আর একটি acceptance check যোগ করলাম যা encoding যাচাই করে।” R: “বাগটি আর ফেরেনি, আর check-টি এখন setup-এর অংশ। শিখলাম, error message-এর শেষ লাইনই সবসময় কারণ নয়।”

প্র ৩৩“কোন কাজটি শেষ করতে পারেননি, আর কেন?” (মাঝারি)

দুর্বল উত্তর: “সব শেষ করেছি,” অথবা দোষ সময় বা টুলের ঘাড়ে।

শক্ত উত্তর: S: “ক্যাপস্টোনে ২৪টি acceptance check লিখেছিলাম।” T: “Deadline-এর আগে কোনটি বাদ যাবে, তা ঠিক করা আমার দায়িত্ব।” A: “টাকা, stock আর webhook-এর check আগে শেষ করলাম, কারণ সেখানে ভুলের দাম সবচেয়ে বেশি। বাকি পাঁচটি, যেমন login-এর rate limit আর README, README-র ‘জানা সীমাবদ্ধতা’ অংশে খোলাখুলি লিখলাম।” R: “১৯টি পাস, ৫টি লেখা ফাঁক। নিজের rubric-এ ১০০-তে ৭৫।” শিক্ষা: অগ্রাধিকার ঝুঁকি দেখে, আর অসম্পূর্ণতা লুকানোর চেয়ে লিখে রাখা ভালো।

প্র ৩৪“AI tool ব্যবহার করেন? কীভাবে?” (মাঝারি)

দুর্বল উত্তর: “না, কখনো না,” যদি তা সত্য না হয়; অথবা “হ্যাঁ, পুরো feature AI-কে দিয়ে লেখাই।”

শক্ত উত্তর: সত্যিটা বলুন, আর যাচাইয়ের পদ্ধতি দিন: “ছোট, যাচাইযোগ্য টুকরো চাই, version বলে দিই। প্রতিটি API আমার version-এর docs-এ মিলিয়ে দেখি, diff পড়ি, আর একটি test আগে ব্যর্থ হতে দেখে তারপর পাস করাই। একবার AI একটি route handler দিয়েছিল যেখানে login যাচাই ছিল, কিন্তু মালিকানার যাচাই ছিল না; আমার test matrix সেটি ধরেছে।” শেষ বাক্যটি শুধু তখনই বলুন, যদি সত্যিই ঘটে থাকে। ইন্টারভিউর সময় AI ব্যবহারের নিয়ম আগে জেনে নিন। রিভিশন: পর্ব ৮৬.

প্র ৩৫“দলের কারও সাথে কারিগরি মতভেদ হলে কী করেন?” (মাঝারি)

দুর্বল উত্তর: “আমি সাধারণত ঠিক থাকি, তাই বুঝিয়ে দিই।”

শক্ত উত্তর: “আগে তাঁর যুক্তি নিজের ভাষায় ফিরিয়ে বলি। তারপর দুজনে ঠিক করি কী মাপলে উত্তর বের হবে: একটি ছোট prototype, একটি load test, বা docs. সিদ্ধান্ত আমার মতের বিপক্ষে গেলেও পুরো মন দিয়ে সেই পথে কাজ করি, আর কারণটি একটি ছোট ADR-এ লিখে রাখি।” দলের অভিজ্ঞতা না থাকলে ক্লাসের প্রকল্প বা open source-এর review-এর সত্যি ঘটনা বলুন। রিভিশন: পর্ব ৮৫.

Portfolio-র প্রকল্প নিয়ে কীভাবে কথা বলবেন

“আপনার প্রকল্পটি দেখান” শুনলে feature-এর তালিকা নয়, তিন মিনিটের একটি গল্প বলুন, এই ক্রমে:

  1. সমস্যা আর ব্যবহারকারী, এক বাক্যে: “ফোনে দেশি পণ্য কেনার একটি ছোট দোকান, বাংলায়।”
  2. ছবি: README-র architecture আর ER ডায়াগ্রাম; কোন অংশ কোথায় চলে।
  3. দুটি কঠিন সিদ্ধান্ত আর কী ছেড়ে দিলেন: “দাম পয়সায় পূর্ণসংখ্যা”, “session, JWT নয়, কারণ একটিই browser app”.
  4. প্রমাণ: test-এর সংখ্যা আর ধরন, একটি concurrency test, Lighthouse-এর মাপ। ক্যাপস্টোনের reference app-এ ২১টি Vitest আর ৩টি Playwright test, accessibility ১০০; নিজের প্রকল্পে নিজের সংখ্যা।
  5. জানা সীমাবদ্ধতা আর পরের ধাপ: যা এখনো নেই, কেন, আর কীভাবে করবেন।

যেকোনো লাইন দেখিয়ে “এটা কেন?” জিজ্ঞেস করা হতে পারে; ব্যাখ্যা করতে পারলে তবেই সেটি আপনার কাজ। Repo-তে কোনো secret বা আসল ক্রেতার তথ্য রাখবেন না।

ইন্টারভিউয়ারকে আপনার প্রশ্ন

  • একটি সাধারণ feature idea থেকে production-এ যেতে কত দিন লাগে, আর পথে কোন কোন review হয়?
  • Test আর CI কেমন? একটি PR merge-এর আগে কী কী চলে?
  • শেষ বড় incident কী ছিল, আর তারপর কী বদলেছে?
  • On-call আছে কি? থাকলে কীভাবে ভাগ হয়?
  • নতুন কেউ এলে প্রথম তিন মাসে ভালো কাজ কেমন দেখায়?

১০ · শেষ checklist, অভিনন্দন আর পরের পথ

পুরো কোর্সের মানচিত্র: কোর্স হোম

ইন্টারভিউর আগের সপ্তাহ

অভিনন্দন

আপনি ৯৩টি পর্ব পেরিয়ে এসেছেন। একটি অনুরোধের যাত্রা থেকে শুরু করে React-এর component, Node আর Express-এর backend, Prisma আর PostgreSQL, login আর অনুমতি, Next.js, real-time আর queue, test, deployment, শেষে পুরো একটি দোকান। এখন আপনি রেস্তোরাঁর ওয়েটার আর রান্নাঘর দুটোই চেনেন, আর ভিড়ের রাতে কোথায় আটকাবে তাও আগে থেকে ভাবতে পারেন। সবচেয়ে বড় অর্জন কোনো framework-এর নাম নয়, একটি অভ্যাস: কিছু বানালেই জিজ্ঞেস করা, দুবার এলে কী হবে, একসাথে এলে কী হবে, আর আমরা কীভাবে জানব।

এরপর কী করবেন

  • ক্যাপস্টোন শেষ করুন বা নিজের একটি প্রকল্প বানান, rubric দিয়ে মাপুন, আর ফাঁকগুলো README-তে লিখুন (পর্ব ৮৮)।
  • মডিউল ১৪-এর বাকি পাতা: পর্ব ৮৯-এর numerical, পর্ব ৯০-এর MCQ, পর্ব ৯১-এর লিখিত ও ভাইভা, আর পর্ব ৯২-এর React ইন্টারভিউ, পাশাপাশি।
  • Primary source পড়ার অভ্যাস: নিজের version-এর official docs, RFC (যেমন HTTP-র RFC 9110, problem details-এর RFC 9457), আর প্রকাশিত postmortem.
  • ক্ষেত্র বাড়ান: System Design, DBMS আর SQL, Cybersecurity, Cloud আর DevOps.
  • Open source-এ ছোট অবদান: docs ঠিক করা, একটি test যোগ, একটি ছোট bug. Review-এর মন্তব্য থেকে শেখা সবচেয়ে দ্রুত।

এই কোর্স বা এই পাতা কোনো চাকরির নিশ্চয়তা দেয় না; নিয়োগ নির্ভর করে অনেক কিছুর ওপর, যার অনেকটাই আপনার হাতে নেই। যা আপনার হাতে আছে, তা হলো যাচাই করা যায় এমন কাজ আর সৎভাবে তা বলতে পারা। সেটুকু এখন আপনার কাছে আছে।

AI-এর যুগে ইন্টারভিউর প্রস্তুতি

AI একজন ধৈর্যশীল নকল ইন্টারভিউয়ার হতে পারে, কিন্তু backend-এ তার ভুলগুলো পরিচিত। সে প্রায়ই পুরোনো API দেয়: Next.js-এর pages router-এর getServerSideProps, Express 4-এর ধরন, Prisma-র পুরোনো generator. না-থাকা package বা option বানায়। তার লেখা কোড চলে, কিন্তু মালিকানার যাচাই বাদ দেয়, secret কোডে লেখে, বা error catch করে চুপচাপ গিলে ফেলে। দরকার ছাড়াই dependency যোগ করে। আর সংখ্যা নিয়ে অতিরিক্ত আত্মবিশ্বাসী থাকে। Live ইন্টারভিউতে AI-এর নিয়ম আগে জেনে নিন।

AI-কে উত্তর লিখতে না বলে আপনাকে চেপে ধরতে বলুন। আসল প্রতিষ্ঠানের কোড, log, key বা ব্যবহারকারীর ডেটা কখনো দেবেন না।

তুমি একজন কড়া কিন্তু ভদ্র backend ইন্টারভিউয়ার। আমার stack: Node.js, Express 5, PostgreSQL, Prisma 7, Next.js 16. আমার স্তর: [শুরু/মাঝারি/উন্নত]। ১. আমাকে একটি ছোট backend কাজ দাও (যেমন rate limiter বা idempotent endpoint). শুধু সমস্যা বলো, সমাধান নয়। ২. আমি আগে প্রশ্ন করব, তারপর সমাধান বলব। আমি edge case, concurrency বা একাধিক process-এর কথা না বললে ফলো-আপ করো, নিজে বলে দিও না। ৩. তারপর একটি debugging প্রশ্ন দাও: একটি উপসর্গ, যেমন "stock মাঝে মাঝে ঋণাত্মক"। আমি কোন প্রমাণ খুঁজব, তা জিজ্ঞেস করো। ৪. শেষে পাঁচটি ক্ষেত্রে ১ থেকে ৫ নম্বর দাও: প্রশ্ন করা, সঠিকতা, test, trade-off, যোগাযোগ। ৫. কোনো API, version বা সংখ্যা নিয়ে নিশ্চিত না হলে "নিশ্চিত নই" লেখো, আর কোন official docs দেখব বলো।

নিজেকে যাচাই করুন

ভুল হলে কারণসহ ব্যাখ্যা পাবেন, আবার চেষ্টাও করতে পারবেন।

ক্রমBackend live coding-এ কাজের ধাপগুলো সঠিক ক্রমে সাজান (▲▼ চেপে সরান)

  1. সীমা, key আর পরিবেশ নিয়ে প্রশ্ন করা
  2. স্বাভাবিক, সীমানার আর ভুলের তিনটি উদাহরণ বলা
  3. Memory-তে store রেখে সরল, চলার মতো সমাধান লেখা
  4. উদাহরণগুলো চালিয়ে test করা
  5. একাধিক process, স্থায়ী store আর ব্যর্থতার কথা বলা

(সহজ) একটি দোকানের API-তে login করা ক্রেতা রহিম GET /api/orders/2042 চাইলেন, কিন্তু অর্ডারটি করিমের। Query-তে মালিকের শর্ত বসানোর পরে কোন উত্তরটি সবচেয়ে উপযুক্ত?

  • 401 Unauthorized
  • 200, অর্ডারের তথ্যসহ
  • 404 Not Found
  • 500 Internal Server Error

(মাঝারি) একটি অর্ডার API Idempotency-Key সমর্থন করে। একই ব্যবহারকারী একই key দিয়ে আগে qty: 1 পাঠিয়ে সফল হয়েছিলেন, এখন একই key দিয়ে qty: 5 পাঠালেন। সবচেয়ে ভালো আচরণ কোনটি?

  • নতুন অর্ডার বানানো, কারণ body আলাদা
  • 422, key ভিন্ন body-তে আবার ব্যবহার হয়েছে বলে
  • আগের জমানো উত্তর নীরবে আবার দেওয়া
  • 409, অনুরোধটি এখনো চলছে বলে

(মাঝারি) পণ্যের তালিকা ORDER BY created_at DESC ধরে cursor দিয়ে ভাগ করা হচ্ছে, আর অনেক পণ্যের created_at একই। কী যোগ করা দরকার?

  • ক্রমের শেষে একটি অনন্য কলাম, যেমন id, আর cursor-এ দুটিই
  • প্রতিটি পাতায় COUNT(*)
  • Cursor বাদ দিয়ে OFFSET
  • limit বাড়িয়ে ১,০০০ করা

(মাঝারি) একটি payment webhook handler আগে JSON.parse(body) করে, তারপর JSON.stringify করে তার ওপর HMAC মিলিয়ে দেখে। মাঝে মাঝে আসল খবরও “bad signature” হচ্ছে। সবচেয়ে সম্ভাব্য কারণ কী?

  • Secret ভুল
  • timingSafeEqual-এর কারণে
  • HTTPS ব্যবহার না করা
  • Raw body-র বদলে আবার বানানো JSON-এর ওপর সই মেলানো

(মাঝারি) কোড প্রথমে stock পড়ে, stock >= 1 হলে আলাদা ধাপে stock - 1 লেখে। ভিড়ে অর্ডার stock-এর চেয়ে বেশি হচ্ছে। সবচেয়ে সরল ও নির্ভরযোগ্য সমাধান কোনটি?

  • কোডটি একটি transaction-এ মুড়ে দেওয়া, আর কিছু না বদলানো
  • UPDATE … SET stock = stock - 1 WHERE id = ? AND stock >= 1, তারপর বদলানো সারির সংখ্যা দেখা
  • Stock Redis cache-এ রাখা
  • পড়া আর লেখার মাঝের কোড দ্রুত করা

(কঠিন) একটি জনপ্রিয় পণ্যের সারিতে প্রতিটি checkout transaction গড়ে ২০ ms lock ধরে রাখে। ওই এক পণ্যে সেকেন্ডে সর্বোচ্চ প্রায় কতটি checkout সম্ভব, আর transaction ৫ ms-এ নামালে?

  • যত server, তত বেশি; সীমা নেই
  • ২০টি, আর ৫ ms-এ ৫টি
  • প্রায় ৫০টি, আর ৫ ms-এ প্রায় ২০০টি
  • প্রায় ১,০০০টি, আর ৫ ms-এ ৫,০০০টি

(মাঝারি) অর্ডার PAID হওয়া আর রসিদের email-এর job দেওয়া, দুটি যেন কখনো আলাদা না হয়। কোন নকশাটি এটি নিশ্চিত করে?

  • একই transaction-এ একটি outbox সারি লেখা, পরে relay worker পাঠায়
  • আগে commit, তারপর queue-তে push
  • আগে queue-তে push, তারপর commit
  • Request-এর ভেতরে await ছাড়া email পাঠানো

(সহজ) কাজের অভিজ্ঞতা নেই এমন একজন প্রার্থী “একটি কঠিন সমস্যার কথা বলুন” প্রশ্নে কোন উত্তরটি দিলে সবচেয়ে ভালো?

  • Internet-এ পড়া একটি অফিসের ঘটনা নিজের বলে বলা
  • “অনেক সমস্যা সমাধান করেছি, নির্দিষ্ট মনে নেই”
  • দলের অন্যের ভুল বলে ঘটনাটি বলা
  • নিজের প্রকল্পের একটি সত্যি bug, STAR কাঠামোয়, ফল আর শিক্ষাসহ

শব্দকোষ

লেখার মধ্যে ডটেড দাগ দেওয়া শব্দে মাউস রাখুন বা চাপুন — অর্থ দেখাবে। এই পর্বের সব নতুন শব্দ এখানে একসাথে:

মনে রাখুন

  1. ধারণার উত্তরে শুধু “কী” নয়, কেন, কখন নয় আর কী দাম; সম্ভব হলে একটি সংখ্যা।
  2. Live coding-এ প্রশ্ন, উদাহরণ, সরল সমাধান, test, production, এই ক্রমে, জোরে ভেবে।
  3. Backend-এর তিন প্রশ্ন সবসময়: দুবার এলে, একসাথে এলে, আর এক process-এর বাইরে গেলে কী হবে।
  4. Debugging-এ অনুমান নয়, প্রমাণ: reproduce, মাপ, তারপর সমাধান আর regression test.
  5. Design-এ সংখ্যা থেকে নকশা; টাকা আর stock-এর সত্য এক transaction-এ, বাকি সব তার পেছনে।
  6. আচরণগত প্রশ্নে শুধু নিজের সত্যি ঘটনা, STAR কাঠামোয়।

পথের শেষ: এটিই এই কোর্সের শেষ পর্ব। ৯৩টি পর্ব পেরিয়ে আসার জন্য অভিনন্দন। কোর্সের মানচিত্রে ফিরে যেতে কোর্স হোম, যেকোনো মডিউল আবার ঝালিয়ে নিতে। ইন্টারভিউর জন্য শুভকামনা।

আরও পড়ুন · ABCL TECH-এ আপনার পরবর্তী পদক্ষেপ

এই পর্বটি শেয়ার করুন:
এই পর্বে ভুল পেয়েছেন? জানান