এই পর্বে যা শিখবেন
- ফুল-স্ট্যাক কাজে AI সহকারী কোথায় সত্যিই কাজের, আর কোথায় চোখ বন্ধ করে মানা বিপজ্জনক
- কাজ থেকে merge পর্যন্ত একটি পদ্ধতি: প্রসঙ্গ, পরিকল্পনা, ছোট ধাপ, চালানো আর পড়া
- এই কোর্স জুড়ে দেখা AI-এর ভুলগুলো এক জায়গায়, প্রতিটির একটি আসল উদাহরণসহ
- অচেনা package install-এর আগে যাচাই, আর typosquatting কেন supply chain-এর ঝুঁকি
- একটি পূর্ণ উদাহরণ: checkout-এ কুপন কোড, একটি নমুনা AI উত্তর, review আর আসল test-এ সংশোধনের প্রমাণ
১ · AI সহকারী কী পারে, কী পারে না
এই কোর্সের প্রায় প্রতিটি পর্ব শেষ হয়েছে “AI-এর যুগে” নামের একটি অংশ দিয়ে। পর্ব ১৩-এ দেখেছেন cleanup ছাড়া effect, পর্ব ২৭-এ Zod 3-এর পুরোনো error.errors, পর্ব ৩৯-এ Prisma 6-এর ধাঁচের client, পর্ব ৫২-এ server আর client-এর সীমানার ভুল, পর্ব ৫৪-এ অনুমতি ছাড়া Server Action, পর্ব ৭২-এ পাস করা অথচ কিছুই না ধরা test, আর পর্ব ৭৮-এ secret-এর নামে NEXT_PUBLIC_. এই পর্ব সেই টুকরোগুলো জোড়া দিয়ে একটি কাজের পদ্ধতি বানাবে।
প্রথমে যন্ত্রটি চিনে নিই। একটি AI coding assistant বিপুল পরিমাণ লেখা আর কোড থেকে শেখা একটি language model-এর ওপর চলে। আপনি একটি prompt দেন, অর্থাৎ লেখা নির্দেশ, আর সে সবচেয়ে সম্ভাব্য পরের লেখাটি তৈরি করে। মূল কথাটি এখানেই: সম্ভাব্য আর সঠিক এক জিনিস নয়। ইন্টারনেটে Next.js-এর Pages Router-এর কোড বহু বছর ধরে জমেছে, তাই সেই ধাঁচের কোড “সম্ভাব্য” মনে হয়, যদিও আপনার project App Router-এ।
দর্জির দোকানের একজন দক্ষ, খুব দ্রুত সহকারীর কথা ভাবুন। হাজারটা জামা সেলাই করেছেন, তাই মাপ না নিয়েও “সাধারণ মাপের” একটি পাঞ্জাবি দশ মিনিটে বানিয়ে দিতে পারেন। কিন্তু খদ্দেরের আসল মাপ তিনি জানেন না, আর ফ্যাশন গত বছরের পর বদলেছে কি না তা-ও না। ওস্তাদের কাজ হলো মাপ বলে দেওয়া, আর জামাটি গায়ে পরিয়ে দেখা। AI সেই সহকারী; মাপ মানে আপনার version আর শর্ত, আর গায়ে পরানো মানে কোড চালিয়ে যাচাই।
| সাধারণত ভালো | সাধারণত দুর্বল |
|---|---|
| পরিচিত ধাঁচের boilerplate: form, CRUD route, test-এর কাঠামো | আপনার নির্দিষ্ট version-এর নতুন API, বিশেষ করে সাম্প্রতিক বদল |
| অচেনা কোড বা error বার্তা ব্যাখ্যা করা | যা সে দেখেনি: আপনার database-এর আসল ডেটা, production-এর config |
| একটি কাজের কয়েকটি বিকল্প পথ দেখানো | নিরাপত্তার অদৃশ্য শর্ত, যেমন “এই action কে ডাকতে পারবেন” |
| ছোট refactor আর নাম বদল, test থাকলে | বড় পরিসরে সিদ্ধান্ত, যেখানে অনেক ফাইলের প্রসঙ্গ লাগে |
| Test-এর জন্য নমুনা ডেটা আর কিনারার মানের তালিকা | নিজের উত্তর যাচাই করা; “পাস করেছে” বললেও না চালিয়ে বলতে পারে |
এখানে কোনো নির্দিষ্ট টুলের নাম নেওয়া হলো না, আর “সেরা” টুল বলেও কিছু নেই। টুলগুলো মাসে মাসে বদলায়; কেউ editor-এর ভেতরে লাইন পূরণ করে, কেউ chat-এ উত্তর দেয়, কেউ agent হিসেবে নিজে ফাইল বদলে command চালায়। এই পর্বের পদ্ধতি সবগুলোতেই খাটে, কারণ এটি টুলের ওপর নয়, প্রমাণের ওপর দাঁড়ানো।
AI-এর উত্তর একটি খসড়া, সিদ্ধান্ত নয়। খসড়াটি আপনার project-এ ঢোকার আগে কয়েকটি ছাঁকনি পার হবে: type check, lint, test, নিরাপত্তার তালিকা আর আপনার নিজের পড়া। প্রতিটি ছাঁকনি আলাদা ধরনের ভুল ধরে। নিচের ছবিতে এই পর্বের আসল উদাহরণগুলো কোন ছাঁকনিতে আটকেছে, দেখুন।
AI-এর খসড়া থেকে merge: কোন ছাঁকনি কী ধরে
Each gate catches a different kind of mistake (examples from this lesson's lab)
২ · কাজের পদ্ধতি — প্রসঙ্গ, পরিকল্পনা, ছোট ধাপ, চালানো, পড়া
“চেকআউটে কুপন যোগ করো” লিখে পাঠালে AI-কে অনেক কিছু অনুমান করতে হয়: কোন framework, কোন version, দাম টাকায় না পয়সায়, কুপন কতবার ব্যবহার করা যায়, login লাগে কি না। প্রতিটি অনুমান একটি সম্ভাব্য bug. তাই প্রথম ধাপ হলো অনুমানের জায়গা কমানো। একটি ভালো কাজের বিবরণে পাঁচটি জিনিস থাকে:
- Version আর stack: “Next.js 16.4, App Router, React 19, Prisma 7.10.1, Vitest 5.1.” এই এক লাইন পুরোনো API-র বড় অংশ আটকে দেয়।
- বিদ্যমান কোড: যে type, function বা ফাইলের সাথে নতুন কোড মিলবে, সেগুলো। না দিলে AI নিজের মতো field-এর নাম বানায়।
- শর্ত: দাম পূর্ণসংখ্যা পয়সায়, নতুন package নয়, secret client-এ নয়, প্রতিটি server-এর কাজের শুরুতে অনুমতি যাচাই।
- গ্রহণের test: acceptance test, অর্থাৎ কোন কোন ঘটনা পাস করলে কাজ শেষ। যেমন “৫০০ টাকার কম কার্টে EID10 বাতিল”।
- কী চাই না: Pages Router নয়,
middleware.tsনয়,anyনয়।
তারপর কোড নয়, আগে পরিকল্পনা চান: কোন ফাইল বদলাবে, কোন function, কোন test. পরিকল্পনা পড়তে দুই মিনিট লাগে, অথচ ভুল দিকে যাওয়া ৩০০ লাইনের কোড পড়তে এক ঘণ্টা। পরিকল্পনা ঠিক হলে কাজটি ছোট ধাপে ভাগ করুন, প্রতিটি ধাপ এমন যে একবারে চালিয়ে দেখা যায়। নিচের ডায়াগ্রামে একটি কাজের পুরো যাত্রা দেখুন।
- কাজের বিবরণ
version + শর্তঅনুমান কমানো - পরিকল্পনা
ফাইল + testকোড নয়, আগে নকশা - Test আগে
8 failedলাল দিয়ে শুরু - ছোট ধাপ
এক functionচালিয়ে দেখা - Diff পড়া
প্রতিটি লাইনবুঝে গ্রহণ - PR আর merge
CI সবুজঅন্যের চোখ
Stack: Express 5.3, TypeScript 6.0, Vitest 5.1
দাম: পূর্ণসংখ্যা পয়সা (pricePaisa)
EID10: ১০%, সর্বোচ্চ 20000, কার্ট ≥ 50000
শর্ত: login লাগবে, কার্ট নিজের হতে হবে
নতুন package নয়
১. src/coupon.ts: applyCoupon(items, code, coupons, used, now)
২. src/app.ts: POST /api/checkout/coupon
৩. tests/coupon.test.ts, tests/api.test.ts
প্রশ্ন: now কেন argument? → test-এ সময় ঠিক রাখতে
' eid10 ' → ছাড় 9000, মোট 81000
500000 → ছাড় 20000 (সীমা)
2026-11-11T18:00:00Z → expired
এখনো কোড নেই → সব লাল, এটাই প্রত্যাশিত
ধাপ ক: applyCoupon → npx vitest run tests/coupon.test.ts
ধাপ খ: route → npx vitest run tests/api.test.ts
প্রতিটির পর: npx tsc --noEmit && npx eslint src
+ logs.push(JSON.stringify({ headers: req.headers }))
↑ cookie log-এ যাবে। কেন?
+ "dependencies": { "coupon-utils": "^1.0.0" }
↑ নতুন package চাইনি
CI: typecheck ✓ lint ✓ test 13/13 ✓ বিবরণে: “AI-এর সাহায্যে লেখা; নিয়ম আর test আমার” Reviewer: অনুমতির পথ আর সময়ের সীমা আলাদা করে দেখলেন
- কাজের বিবরণ। Version, দামের একক, নিয়ম আর “নতুন package নয়”, এই কয়েকটি লাইনই AI-এর সবচেয়ে বড় অনুমানগুলো সরিয়ে দেয়। যা লেখেননি, AI তা নিজে বানাবে।
- আগে পরিকল্পনা। কোন ফাইল, কোন function, কোন test, শুধু এটুকু চান। এখানে প্রশ্ন করা সস্তা:
nowকেন argument, এর উত্তর জানলে বোঝেন test-এ সময় কীভাবে ঠিক রাখা হবে। - Test আগে। নিয়মগুলো হাতে হিসাব করা সংখ্যায় test হয়ে গেল। কোড না থাকায় সব লাল; এটাই প্রমাণ যে test সত্যিই কিছু যাচাই করছে।
- ছোট ধাপ। আগে শুধু হিসাবের function, চালিয়ে দেখা; তারপর route. প্রতিটি ধাপের পর type check আর lint. একসাথে ৫টি ফাইল বদলালে কোন বদলে কী ভাঙল, বোঝা কঠিন।
- Diff পড়া। এই ধাপে দুটি ঝুঁকি চোখে পড়ল: header-সহ log আর একটি অচেনা package. যে লাইন বোঝেন না, সেটি জিজ্ঞেস করুন বা মুছুন; গ্রহণ করবেন না।
- PR আর merge. CI সব যাচাই আবার চালায়, আর একজন মানুষ নিয়মগুলো পড়েন। AI-এর সাহায্য নিলে PR-এর বিবরণে সেটি জানানো ভালো অভ্যাস, যাতে reviewer জানেন কোথায় বাড়তি সতর্কতা দরকার।
এই পদ্ধতির দুটি নিয়ম আলাদা করে মনে রাখুন। সব চালান: AI যদি বলে “এটি কাজ করবে” বা “test পাস করেছে”, অথচ সে কোডটি চালায়নি, সেই কথার কোনো মূল্য নেই। প্রতিটি লাইন পড়ুন: আপনার নামে যে কোড merge হয়, তার দায় আপনার। AI-এর লেখা বলে ভুল ক্ষমাযোগ্য হয় না, ক্রেতার টাকা কাটা গেলে কেউ জিজ্ঞেস করবেন না কোডটি কে লিখেছিল।
৩ · পুরোনো API — সবচেয়ে পরিচিত ভুল, আসল run-এ
AI-এর শেখার উপাদানে পুরোনো version-এর কোড বেশি, কারণ সেগুলো বছরের পর বছর ধরে লেখা হয়েছে। Framework দ্রুত বদলায়, তাই AI আত্মবিশ্বাসের সাথে গত version-এর কোড লেখে। ল্যাবে ইচ্ছা করে পুরোনো ধাঁচের কোড লিখে আসল টুলে চালালাম। লেখার সময়ের version: Node 24.15.0, Next.js 16.4.5, React 19.3.1, TypeScript 6.0.3, Prisma 7.10.1.
Next.js: middleware.ts আর params.id
একটি project-এ পুরোনো নামের middleware.ts আর Next.js 14-এর ধাঁচে params.id সরাসরি পড়া একটি পাতা রাখলাম। সত্যিকারের run:
~/old-next$ npx next build
▲ Next.js 16.4.5 (Turbopack)
⚠ The "middleware" file convention is deprecated. Please use "proxy" instead.
To migrate automatically, run:
npx @next/codemod@canary middleware-to-proxy .
Learn more: https://nextjs.org/docs/messages/middleware-to-proxy
Creating an optimized production build ...
✓ Compiled successfully in 1155ms
Running TypeScript ...
…
Finished TypeScript in 1012ms ...
…
Route (app)
┌ ○ /
├ ○ /_not-found
└ ƒ /products/[id]
ƒ Proxy (Middleware)
Build সফল, শুধু একটি সতর্কবার্তা। proxy.ts হলো Next.js 16-এ middleware-এর নতুন নাম; পুরোনো নাম এখনো চলে কিন্তু deprecated. বড় বিপদটি অন্য জায়গায়: params.id নিয়ে build কিছুই বলেনি, কারণ পাতায় type লেখা ছিল { params: { id: string } }, আর TypeScript সেই ভুল ঘোষণাকেই মেনে নিয়েছে। next start চালিয়ে /products/7 চাইলাম:
~/old-next$ npx next start -H 127.0.0.1 -p 47861
GET http://127.0.0.1:47861/products/7
status 200 · x-shop: bazarghar
<h1>পণ্য </h1>
পাতা খুলল, status 200, কিন্তু পণ্যের নম্বর নেই। Next.js 15 থেকে params একটি Promise, তাই params.id হলো undefined. চলে, অথচ ভুল: AI-এর কোডের সবচেয়ে বিপজ্জনক রূপ। এখন Next.js 16-এর তৈরি করা route-সচেতন type PageProps<'/products/[id]'> দিয়ে একই ভুল লিখে next typegen আর tsc চালালাম:
~/old-next$ npx next typegen
✓ Types generated successfully
~/old-next$ npx tsc --noEmit
app/products/[id]/page.tsx(3,33): error TS2339: Property 'id' does not exist on type 'Promise<{ id: string; }>'.
এবার type check ধরল। শিক্ষাটি দুই স্তরের। এক, পুরোনো API ধরার জন্য type লাগে, কিন্তু type নিজে হাতে বানালে AI ভুল type-ও বানাতে পারে; framework-এর দেওয়া type বেশি নির্ভরযোগ্য। দুই, const { id } = await props.params লেখার পর একই tsc শূন্য error দিল। Routing-এর এই বদলগুলোর বিস্তারিত আছে পর্ব ৫১ আর পর্ব ৫৭-এ।
Prisma: version 6-এর schema, version 7-এর CLI
অনেক পুরোনো উত্তর schema-র ভেতরে database URL লেখে। Prisma 7.10.1-এ সত্যিকারের run:
~/old-prisma$ npx prisma validate
Prisma schema loaded from prisma\schema.prisma.
Error: Prisma schema validation - (validate wasm)
Error code: P1012
error: The datasource property `url` is no longer supported in schema files. Move connection URLs for Migrate to `prisma.config.ts` and pass either `adapter` for a direct database connection or `accelerateUrl` for Accelerate to the `PrismaClient` constructor. …
--> prisma\schema.prisma:8
8 | url = env("DATABASE_URL")
Validation Error Count: 1
এখানে টুল নিজেই পথ দেখাচ্ছে: URL যাবে prisma.config.ts-এ, আর client বানাতে লাগবে একটি driver adapter. বিস্তারিত পর্ব ৩৯-এ। Error বার্তা পুরোটা পড়ার অভ্যাস এখানে কাজে লাগে; AI-কে শুধু “Prisma কাজ করছে না” বললে সে হয়তো আবার পুরোনো পথই দেখাবে।
যেগুলো নীরবে ভুল করে: Vitest আর MSW
কিছু বদল error দেয় না, শুধু আচরণ বদলায়। পর্ব ৭৩-এর ল্যাবে দেখেছেন, Vitest 5-এ clearMocks-এর ডিফল্ট true; Vitest 4-এর ধাঁচে লেখা test, যা আগের test-এর mock-এর ডাক গুনে চলত, নতুন version-এ অন্য ফল দেয়। আর পর্ব ৭৭-এর আসল run-এ MSW 3 পুরোনো option onUnhandledRequest নীরবে উপেক্ষা করেছিল, ফলে test-এর অনুরোধ সত্যিই network-এ বেরিয়ে গিয়েছিল। নতুন নাম onUnhandledFrame, আর tsc সেটি TS2353 দিয়ে ধরেছিল, যদি setup ফাইলও type check-এর আওতায় থাকে।
এ ধরনের নিয়ম মুখস্থ রাখা কঠিন, তাই অনেক দল একটি তালিকা রাখে আর যন্ত্রকে দিয়ে খোঁজায়। নিচে একটি শেখার মডেল: কয়েকটি ফাইলের কোডে পুরোনো ধাঁচ খোঁজে। এটি আসল linter নয়, শুধু ধারণা।
// Teaching model: flag code that was right in an older version.
const rules = [
{ file: /middleware\.ts$/, msg: 'Next.js 16: rename to proxy.ts' },
{ re: /from ['"]next\/router['"]/, msg: 'Pages Router import; App Router uses next/navigation' },
{ re: /getServerSideProps|getStaticProps/, msg: 'Pages Router data function; await in a Server Component' },
{ re: /\bparams\.\w+/, msg: 'Next.js 15+: params is a Promise; await it first' },
{ re: /url\s*=\s*env\(/, msg: 'Prisma 7: connection URL moves to prisma.config.ts' },
{ re: /new PrismaClient\(\)/, msg: 'Prisma 7: pass a driver adapter' },
{ re: /onUnhandledRequest/, msg: 'MSW 3: option is now onUnhandledFrame' },
{ re: /\brest\.(get|post)\(/, msg: 'MSW 2+: use http.get / http.post' },
{ re: /useFormState/, msg: 'React 19: renamed to useActionState' },
{ re: /\.error\.errors\b/, msg: 'Zod 4: use error.issues' },
];
const files = [
{ name: 'middleware.ts', code: 'export function middleware(req) {}' },
{ name: 'app/product/[id]/page.tsx',
code: "import { useRouter } from 'next/router';\nexport default function P({ params }) {\n return params.id;\n}" },
{ name: 'prisma/schema.prisma', code: 'datasource db {\n provider = "postgresql"\n url = env("DATABASE_URL")\n}' },
{ name: 'tests/setup.ts', code: "server.listen({ onUnhandledRequest: 'error' });" },
{ name: 'src/price.ts', code: 'export const add = (a, b) => a + b;' },
];
let total = 0;
for (const f of files) {
const hits = [];
for (const r of rules) {
if (r.file && r.file.test(f.name)) hits.push(` file ${r.msg}`);
if (!r.re) continue;
f.code.split('\n').forEach((line, i) => {
if (r.re.test(line)) hits.push(` L${i + 1} ${r.msg}`);
});
}
total += hits.length;
console.log(hits.length ? `${f.name}\n${hits.join('\n')}` : `${f.name} ok`);
}
console.log(`${total} warnings. A rule list only knows what someone wrote into it.`);আউটপুটে middleware.ts-এর পাশে file Next.js 16: rename to proxy.ts, পাতার ফাইলে দুটি: L1 Pages Router import; App Router uses next/navigation আর L3 Next.js 15+: params is a Promise; await it first. Schema-র L3-এ Prisma 7-এর সতর্কতা, setup ফাইলে MSW 3-এর। src/price.ts ok, আর শেষে 5 warnings. শেষ লাইনটিই আসল শিক্ষা: তালিকা শুধু সেই ভুল ধরে, যা কেউ আগে লিখে রেখেছে। পরের মাসের নতুন বদল এতে নেই, তাই আপনার version-এর release notes আর migration guide পড়ার বিকল্প এটি নয়। লেখার সময়ের Next.js 16 project-এ create-next-app একটি AGENTS.md রাখে, যা AI agent-কে node_modules/next/dist/docs/-এর docs পড়তে বলে, অর্থাৎ install করা version-এর docs project-এর ভেতরেই থাকে।
৪ · বানানো package আর typosquatting
AI কখনো এমন package-এর নাম দেয়, যা কোথাও নেই। নামটি বিশ্বাসযোগ্য শোনায়, যেমন next-coupon-guard, কিন্তু কেউ কখনো প্রকাশ করেনি। একে বলে package hallucination. ২০২৪ সালে প্রকাশিত আর USENIX Security ২০২৫-এ গৃহীত Spracklen ও সহকর্মীদের গবেষণা অনুযায়ী, তাঁদের পরীক্ষায় কোড-লেখা মডেলগুলোর প্রস্তাবিত package-এর প্রায় ১৯.৭% ছিল অস্তিত্বহীন, আর অনেক বানানো নাম বারবার একই রকম ফিরে আসত। এই হার মডেল আর সময়ভেদে বদলায়; সংখ্যাটি নয়, ঝুঁকির ধরনটি মনে রাখুন।
ঝুঁকিটি কোথায়? বানানো নাম install করলে আজ শুধু E404 আসে। কিন্তু নামটি যদি বারবার একই রকম আসে, একজন আক্রমণকারী সেই নামে ক্ষতিকর package প্রকাশ করে রাখতে পারেন। তখন পরের কেউ AI-এর পরামর্শ মেনে install করলে আক্রমণকারীর কোড তাঁর যন্ত্রে চলে। নিরাপত্তা জগতে ২০২৫ সালে এর একটি নাম চালু হয়েছে, “slopsquatting”, যা পুরোনো typosquatting-এর আত্মীয়: জনপ্রিয় নামের সামান্য ভুল বানানে package রাখা। ২০১৭ সালে npm থেকে crossenv নামের একটি package সরানো হয়েছিল, যা জনপ্রিয় cross-env-এর নামের নকল ছিল আর environment variable চুরি করত বলে npm জানিয়েছিল। এ দুটিই supply chain attack-এর রূপ; বড় ছবি আছে পর্ব ৫০ আর Supply chain পর্বে।
Install নয়, আগে npm view
npm view registry থেকে একটি package-এর তথ্য পড়ে, কিছু install করে না, কোনো script চালায় না। তিনটি নামে সত্যিকারের run (npm 11.12.1, ৪ নভেম্বর ২০২৬):
$ npm view zod name version license repository.url maintainers time.created time.modified
name = 'zod'
version = '4.6.7'
license = 'MIT'
repository.url = 'git+https://github.com/colinhacks/zod.git'
maintainers = 'colinhacks <…>'
time.created = '2020-03-07T21:19:15.387Z'
time.modified = '2026-10-12T16:29:38.042Z'
$ npm view zod versions --json # গুনে দেখা হলো
1013
$ npm view bazarghar-coupon-validator
npm error code E404
npm error 404 Not Found - GET https://registry.npmjs.org/bazarghar-coupon-validator - Not found
npm error 404 The requested resource 'bazarghar-coupon-validator@*' could not be found or you do not have permission to access it.
$ npm view expresss
expresss@0.0.0 | ISC | deps: none | versions: 1
temp test
maintainers:
- …
published over a year ago by …
তিনটি ফল তিনটি শিক্ষা দেয়। zod: ছয় বছরের পুরোনো, এক হাজারের বেশি version, পরিচিত repository, সর্বশেষ version এক মাসের কম পুরোনো; বিশ্বাসযোগ্য চিহ্ন, তবু docs আর repository নিজে খুলে মিলিয়ে নিন। bazarghar-coupon-validator: E404, নামটি নেই; AI-এর প্রস্তাব হলে সেটি বানানো। expresss, এক অক্ষর বেশি: নামটি আছে! এটি অন্য একজনের প্রকাশ করা একটিমাত্র version-এর package, বিবরণ “temp test”, Express-এর সাথে কোনো সম্পর্ক নেই। এটি ক্ষতিকর এমন কোনো ইঙ্গিত নেই, আর আমরা install-ও করিনি। শিক্ষাটি হলো, টাইপের একটি ভুলে আপনি সম্পূর্ণ অন্য কারও কোড চাইতে পারেন, আর npm install কোনো আপত্তি করবে না। (Maintainer-এর নাম আর ইমেইল এখানে “…” করে রাখা হলো।)
নামের মিল যন্ত্র দিয়েও মাপা যায়। Edit distance বলে একটি শব্দকে আরেকটিতে বদলাতে কতগুলো অক্ষর যোগ, মোছা বা বদল লাগে। নিচের শেখার মডেলটি অনুরোধ করা নামকে আপনার বিশ্বস্ত তালিকার সাথে মেলায়:
// Teaching model of a typosquatting check: compare a requested name
// with names you already trust, using Levenshtein edit distance.
function lev(a, b) {
const row = Array.from({ length: b.length + 1 }, (_, j) => j);
for (let i = 1; i <= a.length; i++) {
let prev = row[0];
row[0] = i;
for (let j = 1; j <= b.length; j++) {
const keep = row[j];
row[j] = Math.min(row[j] + 1, row[j - 1] + 1, prev + (a[i - 1] === b[j - 1] ? 0 : 1));
prev = keep;
}
}
return row[b.length];
}
const trusted = ['express', 'react', 'react-dom', 'next', 'zod', 'prisma', 'vitest',
'supertest', 'msw', 'dayjs', 'axios', 'lodash', 'cross-env'];
const requested = ['zod', 'expresss', 'raect', 'crossenv', 'lodahs', 'next-coupon-guard'];
for (const name of requested) {
if (trusted.includes(name)) { console.log(`OK ${name}`); continue; }
let best = null, dist = Infinity;
for (const t of trusted) {
const d = lev(name, t);
if (d < dist) { dist = d; best = t; }
}
if (dist <= 2) console.log(`LOOKALIKE ${name} ~ ${best} (distance ${dist}) -> fix the spelling`);
else console.log(`UNKNOWN ${name} (nearest ${best}, distance ${dist}) -> check the registry first`);
}OK zod, কারণ নামটি বিশ্বস্ত তালিকায়। LOOKALIKE expresss ~ express (distance 1) আর LOOKALIKE crossenv ~ cross-env (distance 1): একটি অক্ষর যোগ বা মুছলেই আসল নাম। raect আর lodahs-এর দূরত্ব 2, কারণ পাশাপাশি দুটি অক্ষর অদলবদল সাধারণ Levenshtein-এ দুটি বদল হিসেবে গোনা হয়। শেষ লাইন UNKNOWN next-coupon-guard (nearest next, distance 13) -> check the registry first: কোনো চেনা নামের কাছাকাছি নয়, তাই এটি টাইপের ভুল নয়, হয় নতুন package, নয়তো বানানো নাম। দুই ক্ষেত্রেই registry আর repository না দেখে install নয়। Registry নিজেও নতুন নাম প্রকাশের সময় পরিচিত নামের সাথে মিল খোঁজে বলে জানা যায়, যেমন শুধু বিরামচিহ্নে আলাদা নাম আটকানো, তবে সেই ছাঁকনি নিখুঁত নয়।
একটি অচেনা package হাতে এলে যাচাইয়ের ধাপগুলো দেখুন। মানগুলো ওপরের আসল npm view run থেকে।
1AI: npm i next-coupon-guard2npm view next-coupon-guard3npm view expresss4npm view zod … time maintainers5repository, docs, install script6npm i zod → lockfile, npm audit
| নাম | |
| registry-তে | |
| চিহ্ন | |
| সিদ্ধান্ত |
- প্রস্তাব এল। AI একটি কুপন যাচাইয়ের package install করতে বলল। নামটি আগে শোনেননি। এই মুহূর্তে কিছুই চালাবেন না;
npm ipackage-এর install script চালাতে পারে। - নেই।
npm viewদিলE404. নামটি বানানো। কাজটি নিজের কোডে করুন, বা AI-কে বলুন কোনো package ছাড়া লিখতে। বানানো নাম কখনো “পরে কেউ প্রকাশ করলে” install করার জন্য package.json-এ রেখে দেবেন না। - আছে, কিন্তু অন্য জিনিস।
expresss-এ একটি version, বিবরণে লেখা “temp test”, আর Express থেকে দূরত্ব ১, অর্থাৎ টাইপের ভুল। নাম আছে মানেই ঠিক package নয়। - পরিচিত চিহ্ন।
zod: ২০২০ থেকে আছে, এক হাজারের বেশি version, সর্বশেষ release সাম্প্রতিক। বয়স আর ধারাবাহিকতা ভালো চিহ্ন, কিন্তু প্রমাণ নয়; জনপ্রিয় package-এর account-ও দখল হয়েছে। - Repository আর docs. Registry-র repository link খুলে দেখুন সেটি আসল প্রকল্প কি না, docs-এ আপনার দরকারি API আছে কি না, আর
package.json-এpostinstallআছে কি না। - Install আর lockfile. Install-এর পরে lockfile-এর diff পড়ুন: কতগুলো নতুন dependency এল। npm audit চালান। চাইলে ignore-scripts দিয়ে আগে আলাদা ফোল্ডারে পরখ করুন।
৫ · চলে, কিন্তু বিপজ্জনক — secret, অনুমতি, N+1 আর locale
পুরোনো API অন্তত প্রায়ই error দেয়। পরের ভুলগুলো চুপচাপ থাকে: পাতা খোলে, test-এর সুখী পথ পাস করে, আর ক্ষতি হয় অনেক পরে। এগুলো ধরতে আপনাকে ইচ্ছা করে খারাপ অনুরোধ পাঠাতে হয়, বা উত্তরের ভেতরটা পড়তে হয়।
| ভুল | এই কোর্সের আসল উদাহরণ | কীভাবে ধরবেন |
|---|---|---|
| Secret ফাঁস | NEXT_PUBLIC_ নামে secret build-এ জমে যায় (পর্ব ৭৮); এই পর্বের ল্যাবে log-এ পুরো header, cookie-সহ | Build-এর JS আর HTML-এ নকল মান খোঁজা; diff-এ secret scanning |
| Server-এ authorization নেই | Login বা role যাচাই ছাড়া Server Action (পর্ব ৫৪); এখানে অন্যের কার্টে কুপন (IDOR) | Cookie ছাড়া আর অন্য ব্যবহারকারীর cookie দিয়ে অনুরোধ |
| N+1 query | লুপের ভেতরে query, ১০টি অর্ডারে ১১টি SQL (পর্ব ৪২) | Query log আর counter; ডেটা বাড়িয়ে মাপা |
| ফাঁপা test | Branch coverage ১০০%, mutation score ২৩% (পর্ব ৭২) | কোডে ইচ্ছা করে ভুল ঢুকিয়ে দেখা কোনো test লাল হয় কি না |
| Hydration mismatch আর locale | Locale ছাড়া toLocaleString() (পর্ব ৫৬, পর্ব ৫৯) | অন্য time zone-এ চালিয়ে তুলনা; timeZone স্পষ্ট লেখা |
| Error গিলে ফেলা | catch (e) { return [] } (পর্ব ৩৯) | Diff-এ খালি catch খোঁজা |
Locale-এর ভুলটি ল্যাবে এক লাইনে দেখা যায়। একই মুহূর্ত, ১১ নভেম্বর ২০২৬ UTC সন্ধ্যা ৭:৩০, server চলছে UTC-তে আর ক্রেতার browser ঢাকায়। সত্যিকারের run, Node 24.15.0:
$ TZ=UTC node -e "…" # timeZone ছাড়া | timeZone: 'Asia/Dhaka'
server TZ=UTC : 11/11/2026 | ১২/১১/২০২৬
$ TZ=Asia/Dhaka node -e "…"
browser Dhaka : 11/12/2026 | ১২/১১/২০২৬
timeZone না দিলে server বলে ১১ তারিখ, browser বলে ১২ তারিখ; Next.js-এ এই পার্থক্যই hydration mismatch. timeZone: 'Asia/Dhaka' দিলে দুই জায়গায় একই ১২/১১/২০২৬. AI-এর লেখা তারিখের কোডে প্রায়ই locale আর zone কোনোটিই থাকে না, কারণ লেখকের নিজের যন্ত্রে সেটি ঠিকই দেখায়।
Secret-এর ভুল ধরার একটি অংশ যন্ত্রকে দেওয়া যায়। gitleaks-এর মতো scanner পরিচিত key-এর আকার আর এলোমেলোতা খোঁজে। নিচে একটি diff-এর শুধু যোগ করা লাইনে চালানো শেখার মডেল:
// Teaching model of a secret scan over the added lines of a diff.
const diff = `+++ b/src/pay.ts
+const key = "bzg_live_k7Qp2Xv9LmR4tW8nZc3YhJ6d";
+const label = "aaaaaaaaaaaaaaaaaaaaaaaa";
-const old = 1;
+NEXT_PUBLIC_PAYMENT_SECRET=example-secret
+logger.info({ msg: 'coupon', headers: req.headers });
+const url = process.env.DATABASE_URL;`;
function entropy(s) {
const n = {};
for (const ch of s) n[ch] = (n[ch] || 0) + 1;
return Object.values(n).reduce((h, c) => h - (c / s.length) * Math.log2(c / s.length), 0);
}
const rules = [
['known key prefix', (l) => /\b[a-z]{3}_(live|test)_[A-Za-z0-9]{16,}/.test(l)],
['secret in a public variable', (l) => /NEXT_PUBLIC_\w*(SECRET|KEY|TOKEN)/.test(l)],
['logs headers or cookies', (l) => /log\w*[.(].*(headers|cookie)/i.test(l)],
['high-entropy string', (l) => (l.match(/["']([^"']{20,})["']/g) || [])
.some((q) => entropy(q.slice(1, -1)) > 4)],
];
diff.split('\n').forEach((line, i) => {
if (!line.startsWith('+') || line.startsWith('+++')) return;
const hits = rules.filter(([, test]) => test(line)).map(([name]) => name);
console.log(`${hits.length ? 'FLAG' : 'ok '} line ${i + 1}: ${hits.join(', ') || '-'}`);
});
const k = 'k7Qp2Xv9LmR4tW8nZc3YhJ6d';
console.log('entropy of the key part:', entropy(k).toFixed(2), 'bits/char; of "aaaa...":',
entropy('aaaaaaaaaaaaaaaaaaaaaaaa').toFixed(2));ফল: FLAG line 2: known key prefix, high-entropy string, ok line 3: -, FLAG line 5: secret in a public variable, FLAG line 6: logs headers or cookies, ok line 7: -. মুছে ফেলা লাইন ৪ আর +++ শিরোনাম দেখাই হয়নি। শেষ লাইন: key-এর অংশের Shannon entropy 4.58 bit প্রতি অক্ষর, আর একই অক্ষরের সারির 0.00. লাইন ৭ ঠিক আছে, কারণ মানটি environment থেকে পড়া হচ্ছে, কোডে লেখা নেই। লাইন ৫-এর মান example-secret নিরীহ, তবু নিয়মটি নামের কারণেই ধরেছে: NEXT_PUBLIC_ নামের সবকিছু browser-এ যায়। এমন scanner দলে দুই রকম ভুল করে, আসল secret বাদ দেওয়া আর নিরীহকে secret বলা; তাই এটি একটি জাল, দেয়াল নয়।
৬ · যাচাই-ই মূল দক্ষতা
AI লেখার খরচ প্রায় শূন্যে নামিয়ে এনেছে। ফলে দামি হয়ে উঠেছে বিচার: কোনটি ঠিক, কোনটি নিরাপদ, আর কীভাবে প্রমাণ করব। এই কোর্সে যাচাইয়ের যে টুলগুলো শিখেছেন, সেগুলো এখন একসাথে একটি অভ্যাস:
Type check
Type checker বানানো field আর পুরোনো API-র আকৃতির ভুল ধরে, কোড না চালিয়েই। শর্ত: type নিজে সত্যি হতে হবে, any নয়।
Lint
Linter সন্দেহজনক ধাঁচ ধরে: any, অব্যবহৃত মান, effect-এর dependency. AI-এর eslint-disable লাইন আলাদা করে খুঁজুন।
Test, mutation চিন্তাসহ
প্রত্যাশা হাতে হিসাব করা সংখ্যায়। তারপর প্রশ্ন: কোডে একটি >= বদলে > করলে কোন test লাল হবে? উত্তর না থাকলে test দুর্বল।
Network tab আর SQL log
Browser-এর DevTools-এ উত্তরের ভেতরে বাড়তি field আছে কি না; server-এর query log-এ একটি পাতায় কতগুলো SQL চলল।
DevTools · query logTest-এর অংশটি সবচেয়ে প্রতারণাময়, কারণ AI-এর লেখা test প্রায় সবসময় সবুজ। Mutation testing-এর মূল চিন্তা হলো test-কে test করা: কোডে ইচ্ছা করে একটি ছোট ভুল, একটি mutant, ঢুকিয়ে দেখা কোনো test সেটি ধরে কি না। নিচের মডেলে একই নিয়মের একটি ঠিক আর একটি ভুল কোড, আর দুটি test suite:
// A weak test passes a wrong implementation. A stronger one does not.
// Rule: EID10 gives 10% off, rounded to whole paisa, at most 20000 paisa.
const right = (sub) => Math.min(Math.round(sub * 0.1), 20000);
const wrong = (sub) => sub * 0.1; // no rounding, no cap
function check(name, ok) { return (ok ? 'PASS ' : 'FAIL ') + name; }
const weak = (discount) => [
check('discount is positive and below the subtotal',
discount(90000) > 0 && discount(90000) < 90000),
];
const strong = (discount) => [
check('900 taka cart -> 9000 paisa off', discount(90000) === 9000),
check('5000 taka cart -> capped at 20000', discount(500000) === 20000),
check('104999 paisa -> 10500, whole paisa', discount(104999) === 10500),
];
for (const [label, suite, impl] of [
['weak suite, wrong code', weak, wrong],
['strong suite, wrong code', strong, wrong],
['strong suite, right code', strong, right],
]) {
const lines = suite(impl);
const failed = lines.filter((l) => l.startsWith('FAIL')).length;
console.log(`${label}: ${lines.length - failed} passed, ${failed} failed`);
for (const l of lines) console.log(' ' + l);
}
console.log('wrong(500000) =', wrong(500000), ' wrong(104999) =', wrong(104999));weak suite, wrong code: 1 passed, 0 failed: “ছাড় শূন্যের বেশি আর মোটের কম” প্রায় যেকোনো ভুল কোডেও সত্য। strong suite, wrong code: 1 passed, 2 failed: ৯০০ টাকার কার্টে ভুল কোডও ঠিক উত্তর দেয় (এই কারণেই একটিমাত্র “সুখী” উদাহরণ যথেষ্ট নয়), কিন্তু সীমা আর রাউন্ডিংয়ের test লাল। strong suite, right code: 3 passed, 0 failed. শেষ লাইনে ভুলের আকার: wrong(500000) = 50000, সীমার আড়াই গুণ; আর wrong(104999) = 10499.900000000001, ভগ্নাংশ পয়সা, সাথে floating point-এর লেজ। Test-এর শক্তি আসে কিনারার মান থেকে: সীমার ঠিক ওপরে, রাউন্ডিংয়ের ঠিক মাঝে।
নিরাপত্তার অংশে একটি ছোট তালিকা প্রতিটি AI-এর বদলে চালান: (ক) প্রতিটি server-এর কাজের শুরুতে login, তারপর মালিকানা বা role; (খ) দাম, মালিক, ছাড় client থেকে নেওয়া নয়; (গ) input schema দিয়ে যাচাই; (ঘ) কোনো secret client-এর কোডে বা log-এ নয়; (ঙ) CORS বা CSP ঢিলা করে error থামানো নয়; (চ) নতুন dependency থাকলে তার কারণ জানা। পুরো তালিকা আছে পর্ব ৫০-এ, তত্ত্ব Broken access control পর্বে।
৭ · Test আগে লেখানো, আর AI-এর PR review
AI-কে কোড আর test একসাথে লিখতে দিলে একটি সূক্ষ্ম সমস্যা হয়: test প্রায়ই কোডের বর্তমান আচরণ দেখে লেখা হয়। কোড ভুল হলে test-ও সেই ভুল মেনে নেয়। তাই ক্রম উল্টে দিন: test-first. আগে নিয়মগুলো দিয়ে test লেখান, নিজে পড়ে প্রত্যাশিত সংখ্যাগুলো হাতে মিলিয়ে নিন, চালিয়ে দেখুন সব লাল। তারপর কোড লেখান, আর শর্ত দিন “test বদলাবে না”। Test-driven development-এর ধারণা নতুন নয়, Kent Beck ১৯৯০-এর দশকের শেষে Extreme Programming-এর অংশ হিসেবে এটি জনপ্রিয় করেন, আর ২০০২ সালের দিকে এ নিয়ে বই লেখেন; বিস্তারিত আছে TDD ও BDD পর্বে। AI-এর যুগে এর একটি নতুন সুবিধা: test হয়ে যায় আপনার আর AI-এর মধ্যে চুক্তি, যা AI নিজে বদলাতে পারে না।
একটি পরিচিত ফাঁদ: test লাল হলে agent-ধাঁচের টুল কখনো কোড না সারিয়ে test-এর প্রত্যাশা বদলে দেয়, বা test-টিকে .skip করে দেয়। তাই diff-এ test ফাইলের প্রতিটি বদল আলাদা করে পড়ুন। Test-এর প্রত্যাশা বদলানো একটি নিয়মের বদল; সেটি মানুষের সিদ্ধান্ত।
AI-এর লেখা pull request-এর code review মানুষের PR-এর মতোই, তবে কয়েকটি জায়গায় বাড়তি চোখ লাগে। সাধারণ নিয়ম আর review-এর মনস্তত্ত্ব আছে Software Engineering কোর্সের code review পর্বে আর pull request পর্বে। AI-এর PR-এ যেগুলো বিশেষ করে দেখবেন:
| কী দেখবেন | কেন |
|---|---|
| PR-এর আকার | AI সহজে বিশাল diff বানায়; ৮০০ লাইনের PR কেউ মনোযোগ দিয়ে পড়েন না। ছোট ছোট PR চান। |
package.json আর lockfile | নতুন dependency, বানানো নাম, অদরকারি library |
| Test ফাইলের বদল | প্রত্যাশা বদলানো, .skip, .only, বড় snapshot update |
| Error-এর পথ | খালি catch, error গিলে ডিফল্ট মান, // @ts-ignore |
| অনুমতির পথ | প্রতিটি নতুন route বা action-এ login আর মালিকানা |
| অপ্রাসঙ্গিক বদল | কাজের বাইরে format বদল, নাম বদল; আসল বদল চাপা পড়ে |
| দাবি বনাম প্রমাণ | বিবরণে “সব test পাস” লেখা থাকলে CI-এর লগ দেখুন |
৮ · পুরো উদাহরণ — checkout-এ কুপন কোড
এবার সবকিছু একসাথে। কাজ: বাজারঘরের checkout-এ কুপন কোড যোগ করা। ল্যাবে একটি ছোট Express project বানালাম, লেখার সময়ের version: Express 5.3.0, TypeScript 6.0.3, Vitest 5.1.0, supertest 7.3.1, ESLint 10.14.0, typescript-eslint 8.72.1. কোনো AI সেবা ডাকা হয়নি। নিচের prompt একটি উদাহরণ, আর তার পরের “নমুনা AI উত্তর” আমি নিজে লিখেছি, AI-এর সাধারণ ভুলগুলো এক জায়গায় দেখানোর জন্য। এটি কোনো AI-এর আসল উত্তর নয়।
প্রথমে একটি দুর্বল prompt, যেমনটা প্রায়ই লেখা হয়:
এই prompt-এ version নেই, দামের একক নেই, ছাড়ের সীমা নেই, সময়ের সীমা নেই, login-এর কথা নেই। এমন prompt-এ যে ধরনের উত্তর আসতে পারে, তার একটি নমুনা:
// নমুনা AI উত্তর (শেখানোর জন্য লেখা, আসল AI-এর উত্তর নয়)
export function applyCoupon(
items: CartItem[],
code: string,
coupons: Coupon[],
usedByUser: number,
now = new Date(),
): CouponResult {
const coupon = coupons.find((c) => c.code === code);
if (!coupon) return { ok: false, reason: 'not_found' };
if (new Date(coupon.expiresAt) < now) return { ok: false, reason: 'expired' };
const subtotalPaisa = items.reduce((s, i) => s + i.pricePaisa * i.qty, 0);
const discountPaisa =
coupon.kind === 'percent' ? (subtotalPaisa * coupon.value) / 100 : coupon.value;
return { ok: true, subtotalPaisa, discountPaisa, totalPaisa: subtotalPaisa - discountPaisa };
}// নমুনা AI উত্তর (শেখানোর জন্য লেখা)
app.post('/api/checkout/coupon', (req: any, res: any) => {
const { cartId, code } = req.body;
logs.push(JSON.stringify({ msg: 'apply coupon', headers: req.headers, body: req.body }));
const cart = carts.get(cartId);
if (!cart) return res.status(404).json({ error: 'cart not found' });
const used = usage.get(`${cart.userId}:${code}`) ?? 0;
const result = applyCoupon(cart.items, code, coupons, used);
if (!result.ok) return res.status(422).json(result);
res.json(result);
});Review: চোখে পড়া ভুল
কোডটি পরিষ্কার দেখায়, নাম সুন্দর, type আছে। এখন লাইন ধরে পড়ি:
c.code === code:" eid10 "লিখলে মিলবে না। ক্রেতারা ছোট হাতের অক্ষর আর বাড়তি space লেখেন।coupon.expiresAt: আমাদেরCoupontype-এ field-এর নামendsAt; AI নিজে একটি নাম বানিয়েছে।undefinedথেকেnew Dateহয় Invalid Date, আর তার সাথে যেকোনো তুলনাfalse, অর্থাৎ কুপন কখনো মেয়াদোত্তীর্ণ হবে না। শুরুর সময়ও যাচাই হয়নি।usedByUserনেওয়া হয়েছে, কিন্তু কোথাও ব্যবহার হয়নি: একজন ক্রেতা একবারের কুপন বারবার।- সর্বনিম্ন কার্ট আর সর্বোচ্চ ছাড়ের সীমা নেই; শতাংশের ছাড়ে ভগ্নাংশ পয়সা; FLAT50 ছোট কার্টে মোট ঋণাত্মক করে।
- Route-এ login নেই, আর কার্টটি কার, তা দেখা হয়নি: যে কেউ যেকোনো
cartIdদিয়ে চেষ্টা করতে পারেন। - Log-এ পুরো
req.headers, তাতে session cookie. Log যাঁরা পড়তে পারেন, তাঁরা ক্রেতার session পেয়ে যান। req: any: TypeScript-এর পাহারা বন্ধ, input-এর আকার যাচাই নেই।
প্রমাণ: যাচাইয়ের টুলগুলো কী বলে
চোখে দেখা ভুল প্রমাণ নয়। আগে AI-এর ধাঁচের দুর্বল test, যা শুধু দেখে কুপন “কাজ করছে”। সত্যিকারের run:
~/bazarghar-coupon$ npx vitest run tests/coupon.weak.test.ts
RUN v5.1.0 ~/bazarghar-coupon
Test Files 1 passed (1)
Tests 1 passed (1)
সবুজ। এবার নিয়ম থেকে লেখা test: ৮টি নিয়মের unit test আর ৪টি API test, প্রত্যাশা হাতে হিসাব করা। নমুনা উত্তরের ওপর সত্যিকারের run (দীর্ঘ আউটপুট ছাঁটা):
~/bazarghar-coupon$ npx vitest run
❯ tests/coupon.test.ts (8 tests | 8 failed) 12ms
× accepts lower case and spaces
× caps the discount at 200 taka
× rounds to whole paisa
× rejects a cart below 500 taka
× is not valid one second before Dhaka midnight on 1 Nov
× expires exactly at Dhaka midnight on 12 Nov
× can be used once per customer
× never makes the total negative
❯ tests/api.test.ts (4 tests | 4 failed) 45ms
× needs a login
× does not let Bob use Alice's cart
× applies EID10 to the owner cart
× never writes the session cookie to the log
FAIL tests/api.test.ts > needs a login
AssertionError: expected 200 to be 401 // Object.is equality
FAIL tests/api.test.ts > does not let Bob use Alice's cart
AssertionError: expected 200 to be 404 // Object.is equality
FAIL tests/api.test.ts > never writes the session cookie to the log
+ {"msg":"apply coupon","headers":{"cookie":"sid=sess-alice-example", …
FAIL tests/coupon.test.ts > EID10 rules > caps the discount at 200 taka
- "discountPaisa": 20000,
+ "discountPaisa": 50000,
FAIL tests/coupon.test.ts > EID10 rules > rounds to whole paisa
+ "discountPaisa": 10499.9,
+ "totalPaisa": 94499.1,
FAIL tests/coupon.test.ts > FLAT50 > never makes the total negative
+ "discountPaisa": 5000,
+ "totalPaisa": -2000,
…
Test Files 2 failed | 1 passed (3)
Tests 12 failed | 1 passed (13)
১৩টির মধ্যে ১২টি লাল; একমাত্র সবুজটি সেই দুর্বল test. “applies EID10 to the owner cart” লাল হয়েছে expected 422 to be 200 দিয়ে, কারণ test-এ ক্রেতা ছোট হাতে eid10 লিখেছিলেন। Type check আর lint, একই নমুনা কোডে, সত্যিকারের run:
~/bazarghar-coupon$ npx tsc --noEmit
src/coupon.ts(12,23): error TS2339: Property 'expiresAt' does not exist on type 'Coupon'.
~/bazarghar-coupon$ npx eslint src
src/app.ts
9:40 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
9:50 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
✖ 2 problems (2 errors, 0 warnings)
লক্ষ করুন, Vitest test চালানোর সময় type যাচাই করে না, শুধু type মুছে কোড চালায়। তাই expiresAt-এর ভুল test-এ দেখা গেছে আচরণ হিসেবে (কুপন কখনো মেয়াদোত্তীর্ণ হয় না), আর tsc-এ দেখা গেছে কারণ হিসেবে। আবার lint usedByUser-এর অব্যবহার ধরেনি, কারণ ESLint-এর ডিফল্ট নিয়ম শেষ ব্যবহৃত argument-এর আগের argument নিয়ে চুপ থাকে। এজন্যই একাধিক ছাঁকনি লাগে। নিচে ধাপে ধাপে দেখুন, প্রতিটি test নমুনা কোডের কোন লাইনে আটকায়।
10coupons.find((c) => c.code === code)12new Date(coupon.expiresAt) < now15(subtotalPaisa * coupon.value) / 10017totalPaisa: subtotalPaisa - discountPaisa9usedByUser (কখনো পড়া হয় না)Rcarts.get(cartId) — login ছাড়া
| ইনপুট | |
| প্রত্যাশা | |
| নমুনা কোড |
- বড়-ছোট হাতের অক্ষর।
' eid10 'আর'EID10'হুবহু মেলে না, তাইnot_found. Trim আর upper case না করায় আসল ক্রেতা কুপন পাবেন না। - বানানো field.
coupon.expiresAtহলোundefined,new Date(undefined)Invalid Date, আর তুলনাfalse. মেয়াদের পরের মুহূর্তেও কুপনok. - সীমা নেই। ৫০০০ টাকার কার্টে ১০% মানে 50000 পয়সা, কিন্তু নিয়ম বলে সর্বোচ্চ 20000. দোকান প্রতি অর্ডারে বাড়তি ৩০০ টাকা হারাত।
- ভগ্নাংশ পয়সা। 104999-এর ১০% হলো 10499.9. Payment gateway পূর্ণসংখ্যা চায়, আর হিসাবের খাতায় পয়সার ভগ্নাংশ জমতে থাকে।
Math.roundদিলে 10500. - ঋণাত্মক মোট। ৩০ টাকার কার্টে ৫০ টাকার ছাড়ে মোট -2000 পয়সা। ছাড় কখনো subtotal-এর বেশি হবে না, এই নিয়ম কেউ লেখেনি।
- অব্যবহৃত argument.
usedByUserfunction-এ ঢোকে, কিন্তু কোনো শর্তে নেই। একবারের কুপন সীমাহীন। - Login নেই। Route কারও পরিচয় দেখে না, তাই cookie ছাড়া অনুরোধেও 200. আর অন্য ক্রেতার cookie দিয়ে Alice-এর কার্টেও 200, একটি IDOR.
সংশোধিত কোড
Review-এর পর, এবার নিয়ম আর শর্তসহ ভালো prompt দিয়ে বা নিজের হাতে, সংশোধিত কোড। প্রতিটি লাইন একটি নিয়মের সাথে মেলে:
export function normalizeCode(raw: string): string {
return raw.trim().toUpperCase();
}
export function applyCoupon(
items: CartItem[], rawCode: string, coupons: Coupon[], usedByUser: number, now: Date,
): CouponResult {
const coupon = coupons.find((c) => c.code === normalizeCode(rawCode));
if (!coupon) return { ok: false, reason: 'not_found' };
const t = now.getTime();
if (t < Date.parse(coupon.startsAt)) return { ok: false, reason: 'not_started' };
if (t >= Date.parse(coupon.endsAt)) return { ok: false, reason: 'expired' };
if (usedByUser >= coupon.perUserLimit) return { ok: false, reason: 'limit_reached' };
const subtotalPaisa = items.reduce((s, i) => s + i.pricePaisa * i.qty, 0);
if (subtotalPaisa < coupon.minSubtotalPaisa) return { ok: false, reason: 'min_subtotal' };
let discountPaisa =
coupon.kind === 'percent' ? Math.round((subtotalPaisa * coupon.value) / 100) : coupon.value;
if (coupon.maxDiscountPaisa !== undefined) {
discountPaisa = Math.min(discountPaisa, coupon.maxDiscountPaisa);
}
discountPaisa = Math.min(discountPaisa, subtotalPaisa);
return { ok: true, subtotalPaisa, discountPaisa, totalPaisa: subtotalPaisa - discountPaisa };
}app.post('/api/checkout/coupon', (req, res) => {
const userId = sessionUser(req.headers.cookie);
if (!userId) return res.status(401).json({ error: 'login required' });
const { cartId, code } = req.body ?? {};
if (typeof cartId !== 'string' || typeof code !== 'string' || code.length > 32) {
return res.status(400).json({ error: 'invalid input' });
}
const cart = carts.get(cartId);
if (!cart || cart.userId !== userId) return res.status(404).json({ error: 'cart not found' });
const used = usage.get(`${userId}:${normalizeCode(code)}`) ?? 0;
const result = applyCoupon(cart.items, code, coupons, used, new Date());
logs.push(JSON.stringify({ msg: 'apply coupon', userId, cartId, ok: result.ok }));
if (!result.ok) return res.status(422).json(result);
return res.json(result);
});কয়েকটি সিদ্ধান্তের কারণ। অন্যের কার্টে 403 নয়, 404: কার্টটি আছে কি না, তা-ও অন্যকে জানানো হয় না। মেয়াদের শেষ সময় “ঢাকার ১২ নভেম্বরের মধ্যরাত” UTC-তে 2026-11-11T18:00:00Z হিসেবে রাখা, আর তুলনা >=, অর্থাৎ শেষ মুহূর্তটি বাদ। now argument হিসেবে আসে, তাই test-এ সময় ঠিক রাখা যায়। ছোট এই উদাহরণে input যাচাই হাতে লেখা; বড় অ্যাপে পর্ব ২৭-এর মতো Zod schema ভালো। আসল দোকানে ব্যবহারের হিসাব database-এ আর একই transaction-এ বাড়াতে হবে, নইলে একসাথে দুটি অনুরোধে একবারের কুপন দুবার চলতে পারে (পর্ব ৪৩)। সংশোধিত কোডে সত্যিকারের run:
~/bazarghar-coupon$ npx tsc --noEmit && npx eslint src && echo "types and lint ok"
types and lint ok
~/bazarghar-coupon$ npx vitest run --reporter=verbose
✓ tests/coupon.weak.test.ts > applies the coupon 1ms
✓ tests/coupon.test.ts > EID10 rules > accepts lower case and spaces 1ms
✓ tests/coupon.test.ts > EID10 rules > caps the discount at 200 taka 0ms
✓ tests/coupon.test.ts > EID10 rules > rounds to whole paisa 0ms
✓ tests/coupon.test.ts > EID10 rules > rejects a cart below 500 taka 0ms
✓ tests/coupon.test.ts > EID10 rules > is not valid one second before Dhaka midnight on 1 Nov 0ms
✓ tests/coupon.test.ts > EID10 rules > expires exactly at Dhaka midnight on 12 Nov 0ms
✓ tests/coupon.test.ts > EID10 rules > can be used once per customer 0ms
✓ tests/coupon.test.ts > FLAT50 > never makes the total negative 0ms
✓ tests/api.test.ts > needs a login 25ms
✓ tests/api.test.ts > does not let Bob use Alice's cart 5ms
✓ tests/api.test.ts > applies EID10 to the owner cart 3ms
✓ tests/api.test.ts > never writes the session cookie to the log 0ms
Test Files 3 passed (3)
Tests 13 passed (13)
সবুজ test তখনই বিশ্বাসযোগ্য, যখন জানি তারা ভুল ধরতে পারে। তাই শেষ ধাপে সংশোধিত কোডে হাতে চারটি mutant ঢোকালাম, একবারে একটি, প্রতিবার পুরো suite চালিয়ে আবার আসল কোড ফেরত। একটি ছোট node script দিয়ে, সত্যিকারের run:
~/bazarghar-coupon$ node mutate.mjs
KILLED t >= Date.parse(coupon.endsAt) -> t > Date.parse(coupon.endsAt)
FAIL tests/coupon.test.ts > EID10 rules > expires exactly at Dhaka midnight on 12 Nov
KILLED discountPaisa = Math.min(discountPaisa, subtotalPaisa); -> (line removed)
FAIL tests/coupon.test.ts > FLAT50 > never makes the total negative
KILLED !cart || cart.userId !== userId -> !cart
FAIL tests/api.test.ts > does not let Bob use Alice's cart
KILLED usedByUser >= coupon.perUserLimit -> usedByUser > coupon.perUserLimit
FAIL tests/coupon.test.ts > EID10 rules > can be used once per customer
চারটি mutant-ই ধরা পড়ল, আর প্রতিটি ঠিক সেই test-এ, যা ওই নিয়মের জন্য লেখা। মালিকানার শর্ত মুছলে Bob-এর test লাল; এই প্রমাণ ছাড়া “authorization আছে” কথাটি শুধু একটি দাবি। Stryker-এর মতো টুল এই কাজ স্বয়ংক্রিয়ভাবে, শত শত mutant দিয়ে করে (পর্ব ৭২); এখানে হাতে চারটি দিয়ে ধারণাটি দেখা হলো।
৯ · গোপনীয়তা, লাইসেন্স আর শেখা বনাম কাজ ছেড়ে দেওয়া
যা কখনো পেস্ট করবেন না
Prompt-এ যা লেখেন, তা অন্য কারও server-এ যায়। সেবাভেদে সেটি সংরক্ষিত হতে পারে, মানুষ পড়তে পারেন, বা মডেল প্রশিক্ষণে ব্যবহার হতে পারে; নিয়ম জানতে সেই সেবার শর্ত আর আপনার প্রতিষ্ঠানের নীতি পড়ুন। তিনটি জিনিস কখনো এমন টুলে নয়, যেটি ব্যবহারের অনুমতি আপনার নেই: secret (key, password, .env-এর লেখা), ক্রেতার তথ্য (নাম, ফোন, ঠিকানা, অর্ডার; অর্থাৎ PII), আর প্রতিষ্ঠানের গোপন কোড. ২০২৩ সালে প্রকাশিত খবর অনুযায়ী একটি বড় ইলেকট্রনিক্স প্রতিষ্ঠানের কয়েকজন কর্মী অভ্যন্তরীণ source code একটি জনপ্রিয় chat সেবায় পেস্ট করেছিলেন, যার পর প্রতিষ্ঠানটি সাময়িকভাবে এমন টুলের ব্যবহার সীমিত করে। কাজের ধরনটিই ঝুঁকি, কোনো বিশেষ প্রতিষ্ঠান নয়।
বিকল্প সহজ: আসল মানের বদলে example-secret, আসল ক্রেতার বদলে বানানো নমুনা, পুরো repository-র বদলে ছোট একটি অংশ, যাতে গোপন ব্যবসায়িক যুক্তি নেই। আর agent-ধাঁচের টুলকে production database-এর URL বা deploy-এর key দেবেন না; least privilege-এর নীতি AI-এর বেলাতেও খাটে। AI-এর নিজস্ব নিরাপত্তা ঝুঁকি, যেমন prompt injection, নিয়ে বিস্তারিত আছে AI security পর্বে।
লাইসেন্স আর কৃতিত্ব — সতর্ক উত্তর
AI-এর লেখা কোডের মালিকানা আর লাইসেন্স নিয়ে আইন এখনো স্থির নয়, আর দেশভেদে আলাদা। কয়েকটি বিষয় তবু বলা যায়, সতর্কভাবে। এক, open source কোডের নিজস্ব software licence থাকে; কিছু লাইসেন্স কপিরাইট নোটিশ রাখতে বলে, আর GPL-এর মতো কিছু লাইসেন্স সেই কোড থেকে তৈরি কাজও একই শর্তে প্রকাশ করতে বলে। AI যদি কোনো পরিচিত প্রকল্পের কোড প্রায় হুবহু লিখে দেয়, লাইসেন্সের দায় তৈরি হতে পারে। দুই, কিছু টুলে এমন মিল খুঁজে সতর্ক করার বা আটকানোর ব্যবস্থা আছে। তিন, মার্কিন কপিরাইট অফিস ২০২৫ সালের একটি প্রতিবেদনে বলেছে যে শুধু AI-এর তৈরি অংশে কপিরাইট সুরক্ষা পেতে মানুষের সৃজনশীল অবদান লাগে; বাংলাদেশে বা আপনার দেশে নিয়ম কী, তা আইনজ্ঞের কাছ থেকে জানুন। বাস্তব পরামর্শ: প্রতিষ্ঠানের নীতি মানুন, বড় হুবহু অংশ দেখলে উৎস খুঁজুন, আর ক্লায়েন্টের কাজে AI ব্যবহারের নিয়ম চুক্তিতে স্পষ্ট করুন। পেশাগত নৈতিকতার বড় আলোচনা আছে professional ethics পর্বে।
শেখা, না কাজ ছেড়ে দেওয়া
শিক্ষার্থীদের জন্য সবচেয়ে বড় প্রশ্নটি প্রযুক্তিগত নয়। AI দিয়ে একটি অ্যাসাইনমেন্ট দশ মিনিটে শেষ করা যায়, কিন্তু তাতে মাথায় কী থাকল? যে দক্ষতা দিয়ে আপনি AI-এর ভুল ধরবেন, সেই দক্ষতাই তৈরি হয় নিজে ভুল করে, আটকে গিয়ে, error পড়ে। ইন্টারভিউতে বা লিখিত পরীক্ষায় AI থাকে না; থাকে আপনার বোঝা। কয়েকটি অভ্যাস কাজে লাগে:
- আগে নিজে চেষ্টা, অন্তত ২০ মিনিট; তারপর AI-কে সমাধান নয়, ইঙ্গিত চান।
- ব্যাখ্যা চান, উত্তর নয়: “এই error কেন আসছে, সম্ভাব্য তিনটি কারণ বলো; সমাধান লিখবে না।”
- উল্টো দিক থেকে যাচাই: AI-এর ব্যাখ্যা পড়ে নিজের ভাষায় লিখুন, তারপর docs-এর সাথে মেলান।
- AI-কে পরীক্ষক বানান: “React-এর useEffect নিয়ে আমাকে পাঁচটি প্রশ্ন করো, আমার উত্তরের ভুল ধরিয়ে দাও।”
- নিয়মটি জানুন: প্রতিষ্ঠান বা পরীক্ষা AI-এর ব্যবহার নিষেধ করলে সেটি মানা সততার প্রশ্ন।
১০ · AI-এর যুগে: আপনার নিজের কাজের নিয়ম
এই পর্ব নিজেই একটি “AI-এর যুগে” অংশ, তাই এখানে পুরো কোর্সের শিক্ষা একটি prompt আর একটি চেকলিস্টে। প্রথমে কোর্স জুড়ে দেখা AI-এর ভুলের একটি সারাংশ:
| AI-এর ভুল | প্রথম যাচাই |
|---|---|
পুরোনো API: Pages Router, middleware.ts, Prisma 6-এর schema, Vitest 4-এর ডিফল্ট, MSW 2-এর option | নিজের version-এর docs আর migration guide; tsc, build-এর সতর্কবার্তা |
| বানানো package বা option | npm view, docs-এ নাম খোঁজা, type error |
| অদরকারি dependency | Built-in দিয়ে হয় কি না; lockfile-এর diff |
| চলে কিন্তু secret ফাঁস করে | Build আর log-এ নকল মান খোঁজা; scanner |
| Server-এ অনুমতি নেই | Cookie ছাড়া আর অন্যের cookie দিয়ে অনুরোধ |
| Error লুকানো | খালি catch, @ts-ignore, eslint-disable খোঁজা |
| “Test পাস করেছে” দাবি | নিজে চালানো; ইচ্ছা করে একটি ভুল ঢুকিয়ে দেখা |
নিচের prompt-টি যেকোনো ফুল-স্ট্যাক কাজের জন্য কাঠামো হিসেবে নিন। বন্ধনীর জায়গাগুলো নিজের কাজ দিয়ে ভরুন, আর কোনো আসল secret বা ক্রেতার তথ্য দেবেন না।
AI-এর লেখা কোড merge-এর আগে — চেকলিস্ট
আরও গভীরে: AI-এর লেখা কোডের নিরাপত্তা নিয়ে গবেষণা বেড়েছে। Pearce ও সহকর্মীদের “Asleep at the Keyboard?” (IEEE S&P ২০২২) নিরাপত্তা-সংবেদনশীল কিছু পরিস্থিতিতে একটি কোড-সহকারীর প্রস্তাবের বড় অংশে, প্রায় ৪০%, দুর্বলতা পেয়েছিল। Perry ও সহকর্মীদের ব্যবহারকারী-গবেষণা (ACM CCS ২০২৩) জানায়, সহকারী ব্যবহারকারীরা কিছু কাজে কম নিরাপদ কোড লিখেছিলেন অথচ নিজেদের কোডকে বেশি নিরাপদ ভেবেছিলেন। দুটিই নির্দিষ্ট সময় আর মডেলের মাপ; তবে “আত্মবিশ্বাস আর সঠিকতা আলাদা” এই শিক্ষা টুল বদলালেও খাটে।
পরীক্ষা ও ইন্টারভিউতে:
- Package hallucination কেন নিরাপত্তার ঝুঁকি? → বানানো নাম আক্রমণকারী আগেই প্রকাশ করে রাখলে AI-এর পরামর্শে install করা মানে তাঁর কোড চালানো।
- Install-এর আগে package যাচাইয়ের একটি কমান্ড? →
npm view <name>: নেই হলে E404; থাকলে বয়স, version, maintainer আর repository দেখা। - Coverage ১০০% হলেও test দুর্বল হতে পারে কেন? → Coverage বলে লাইন চলেছে, ফল যাচাই হয়েছে কি না বলে না; mutation testing সেটি মাপে।
- AI-কে কাজ দেওয়ার আগে কোন তথ্যগুলো দেবেন? → Version, বিদ্যমান কোড, শর্ত, গ্রহণের test আর কী চাই না।
- AI-এর PR-এ কোন ফাইলের বদল আলাদা করে পড়বেন? →
package.json, lockfile আর test ফাইল।
ভাবনার প্রশ্ন
প্রতিটি প্রশ্ন নিয়ে নিজে কিছুক্ষণ ভাবুন, তারপর “→ উত্তর” চাপুন।
প্র ০১AI সহকারী কি সত্যিই ডেভেলপারকে দ্রুত করে? গবেষণা কী বলে?
উত্তরটি “নির্ভর করে”, আর সেই নির্ভরতাটাই আকর্ষণীয়। প্রথম দিকের একটি নিয়ন্ত্রিত পরীক্ষায়, GitHub-সংশ্লিষ্ট গবেষকদের ২০২৩ সালে প্রকাশিত কাজে, অংশগ্রহণকারীদের JavaScript-এ একটি ছোট HTTP server লিখতে দেওয়া হয়েছিল; সহকারী পাওয়া দল উল্লেখযোগ্যভাবে কম সময়ে শেষ করেছিল। কাজটি ছিল নতুন, ছোট আর সুনির্দিষ্ট, ঠিক যেখানে AI সবচেয়ে ভালো।
২০২৫ সালের জুলাইয়ে METR নামের একটি গবেষণা সংস্থা ভিন্ন ধরনের একটি randomized controlled trial প্রকাশ করে। ১৬ জন অভিজ্ঞ open source ডেভেলপার নিজেদের চেনা, বড়, পরিণত repository-তে ২৪৬টি আসল কাজ করেছিলেন; প্রতিটি কাজে AI ব্যবহারের অনুমতি এলোমেলোভাবে দেওয়া বা না দেওয়া হয়েছিল। ফল চমকে দেওয়ার মতো: AI-সহ কাজে গড়ে প্রায় ১৯% বেশি সময় লেগেছিল। আরও চমকপ্রদ, অংশগ্রহণকারীরা কাজ শেষেও ভাবছিলেন AI তাঁদের প্রায় ২০% দ্রুত করেছে। METR নিজেই সতর্ক করেছে যে ফলটি সব ধরনের কাজে বা পরের প্রজন্মের টুলে খাটবে, এমন দাবি তারা করে না।
দুটি ফল পরস্পরবিরোধী নয়। বড় চেনা codebase-এ অভিজ্ঞ মানুষ এমনিতেই দ্রুত; সেখানে prompt লেখা, অপেক্ষা, আর AI-এর প্রায়-ঠিক উত্তর পড়ে সারানোর খরচ লাভকে ছাড়িয়ে যেতে পারে। অচেনা কাজে, নতুন library শিখতে, boilerplate-এ লাভ বেশি। আর অনুভূতি আর মাপের ফারাক একটি বড় শিক্ষা: “দ্রুত লাগছে” মানেই দ্রুত নয়, ঠিক যেমন “চলছে” মানেই ঠিক নয়।
দলের জন্য বাস্তব প্রশ্নটি তাই “AI ব্যবহার করব কি না” নয়, বরং কোন কাজে, আর মোট সময়ে review আর সংশোধন ধরে মাপলে কী দাঁড়ায়। দ্রুত লেখা কিন্তু ধীর review হলে লাভ review-এর টেবিলে হারিয়ে যায়।
মূল উপলব্ধি: উৎপাদনশীলতা মাপুন শেষ পর্যন্ত, merge আর production পর্যন্ত, শুধু লেখার মুহূর্ত পর্যন্ত নয়; আর নিজের অনুভূতির চেয়ে মাপকে বেশি বিশ্বাস করুন।
প্র ০২AI বানানো package-এর নাম দিলে সমস্যা শুধু install ব্যর্থ হওয়া। তাহলে একে নিরাপত্তার ঝুঁকি বলা কেন?
আজ install ব্যর্থ হয়, কিন্তু নামটি খালি থাকে, আর npm-এর মতো public registry-তে যে কেউ খালি নামে package প্রকাশ করতে পারেন। Spracklen ও সহকর্মীদের গবেষণা (USENIX Security ২০২৫) দেখিয়েছিল, তাঁদের নমুনায় প্রস্তাবিত package-এর প্রায় ১৯.৭% অস্তিত্বহীন ছিল, আর এর মধ্যে দুই লাখের বেশি আলাদা নাম ছিল। গুরুত্বপূর্ণ পর্যবেক্ষণ: অনেক বানানো নাম একই prompt-এ বারবার একই রকম ফিরে আসে। অর্থাৎ নামগুলো অনুমান করা যায়। একজন আক্রমণকারী জনপ্রিয় প্রশ্ন দিয়ে মডেলকে জিজ্ঞেস করে বারবার আসা বানানো নামগুলো সংগ্রহ করতে পারেন, তারপর সেই নামে install script-সহ package প্রকাশ করতে পারেন।
এটি নতুন কৌশলের নতুন রূপ। Typosquatting বহু পুরোনো: ২০১৭ সালে npm crossenv-সহ কয়েকটি package সরিয়েছিল, যেগুলো জনপ্রিয় নামের নকল ছিল আর environment variable পাঠিয়ে দিত বলে জানানো হয়েছিল। ২০২৪ সালে একজন নিরাপত্তা গবেষক AI-এর বারবার প্রস্তাব করা একটি বানানো Python package-এর নামে একটি ক্ষতিহীন খালি package প্রকাশ করে পরীক্ষা করেছিলেন বলে জানিয়েছিলেন; তাঁর হিসাবে সেটি কয়েক মাসে হাজার হাজারবার নামানো হয়েছিল। ২০২৫ সালে Python Software Foundation-এর নিরাপত্তায় কাজ করা Seth Larson এই ধরনকে “slopsquatting” নাম দেন বলে ব্যাপকভাবে উদ্ধৃত।
ঝুঁকিটি install-এর মুহূর্তেই বাস্তব হয়, কারণ npm package-এর postinstall script install-এর সময় আপনার যন্ত্রে, আপনার অধিকারে চলে: আপনার .env, SSH key, CI-এর token সবই নাগালে। AI agent নিজে command চালাতে পারলে মানুষের চোখ পড়ার আগেই এটি ঘটতে পারে।
প্রতিরক্ষা কয়েক স্তরের: নতুন নাম দেখলে npm view, repository আর docs; agent-এর install-এর অনুমতি সীমিত রাখা; CI-তে npm ci আর lockfile; install script বন্ধ রেখে পরখ; আর সদ্য প্রকাশিত version-এ কয়েক দিনের অপেক্ষার নিয়ম।
মূল উপলব্ধি: বানানো নাম আজকের bug, কালকের ফাঁদ। Package-এর নাম একটি বিশ্বাসের সিদ্ধান্ত, আর সেই সিদ্ধান্ত AI-এর হাতে নয়, আপনার হাতে থাকা উচিত।
প্র ০৩AI যদি লাখ লাখ open source প্রকল্প থেকে শেখে, তাহলে তার কোড তো গড়পড়তা ভালো হওয়ার কথা। নিরাপত্তার ভুল কেন হয়?
কারণ “গড়পড়তা” আর “নিরাপদ” এক জিনিস নয়। ইন্টারনেটের কোডের বড় অংশ টিউটোরিয়াল, উত্তর-প্রশ্নের সাইট আর শেখার প্রকল্প, যেগুলো ধারণা দেখাতে লেখা, production-এর জন্য নয়। একটি টিউটোরিয়ালে login যাচাই বাদ দেওয়া হয় যাতে মূল কথাটি পরিষ্কার থাকে; SQL-এ string জোড়া হয় কারণ পড়তে সহজ। মডেল সম্ভাব্য লেখা তৈরি করে, আর এমন কোড প্রচুর বলেই সম্ভাব্য।
গবেষণায় এর ইঙ্গিত আছে। ২০২১ সালে প্রকাশিত আর IEEE S&P ২০২২-এ উপস্থাপিত Pearce ও সহকর্মীদের “Asleep at the Keyboard?” গবেষণায় একটি কোড-সহকারীকে MITRE-এর পরিচিত দুর্বলতার তালিকা থেকে নেওয়া পরিস্থিতিতে কোড লিখতে দেওয়া হয়; উৎপন্ন প্রোগ্রামের প্রায় ৪০% দুর্বল পাওয়া যায়। Perry ও সহকর্মীদের ACM CCS ২০২৩-এর গবেষণায় মানুষের আচরণ মাপা হয়: সহকারী পাওয়া অংশগ্রহণকারীরা কয়েকটি কাজে কম নিরাপদ কোড লিখেছিলেন, অথচ নিজেদের কোড বেশি নিরাপদ বলে বিশ্বাস করেছিলেন। দুটি গবেষণাই নির্দিষ্ট মডেল আর সময়ের; নতুন মডেল হয়তো ভালো, কিন্তু মূল সমস্যাটি কাঠামোগত।
কাঠামোগত কেন? নিরাপত্তার নিয়ম প্রায়ই কোডে অদৃশ্য প্রসঙ্গের ওপর নির্ভর করে: এই action কে ডাকতে পারবেন, এই field কে দেখতে পারবেন, এই মান কোথা থেকে আসে। মডেল আপনার ব্যবসার নিয়ম জানে না, যদি না বলেন। এই পর্বের নমুনা কুপন কোডে প্রতিটি লাইন আলাদাভাবে “যুক্তিসংগত”, তবু login আর মালিকানার প্রশ্নটি কোনো লাইনেই নেই, কারণ prompt-এ ছিল না।
আরেকটি কারণ উৎসাহের দিক: AI-কে প্রায়ই error থামাতে বলা হয়। CORS error এলে origin: true, CSP-তে unsafe-inline, type error-এ any: error চলে যায়, ঝুঁকি থেকে যায় (পর্ব ৫০)।
মূল উপলব্ধি: নিরাপত্তা হলো প্রসঙ্গের নিয়ম, আর প্রসঙ্গ আপনার মাথায়। তাই নিয়মগুলো prompt-এ লিখুন, আর test দিয়ে প্রমাণ করুন, কারণ আত্মবিশ্বাসী চেহারার কোড আর নিরাপদ কোড আলাদা।
প্র ০৪AI-এর লেখা কোড কি নিজের বলে ব্যবহার করা যায়? লাইসেন্স আর কৃতিত্বের প্রশ্ন কতটা গুরুতর?
সৎ উত্তর হলো, আইন এখনো তৈরি হচ্ছে, তাই এখানে নিশ্চিত দাবি না করে প্রশ্নগুলো সাজাই। প্রথম প্রশ্ন মালিকানা: AI-এর তৈরি কোডে কপিরাইট কার? মার্কিন কপিরাইট অফিস ২০২৫ সালে প্রকাশিত একটি প্রতিবেদনে অবস্থান দিয়েছে যে কপিরাইট সুরক্ষার জন্য মানুষের সৃজনশীল অবদান লাগে; শুধু prompt দিয়ে তৈরি অংশ সাধারণত সেই শর্ত পূরণ করে না, তবে মানুষের বাছাই, বিন্যাস আর বদল সুরক্ষিত হতে পারে। অন্য দেশের অবস্থান আলাদা হতে পারে, আর বাংলাদেশের প্রেক্ষাপটে নির্দিষ্ট নিয়ম জানতে আইনজ্ঞের পরামর্শ নিন।
দ্বিতীয় প্রশ্ন উৎসের লাইসেন্স। মডেল কখনো কোনো প্রকল্পের কোড প্রায় হুবহু লিখে দিতে পারে, বিশেষ করে বিখ্যাত ছোট function. সেই প্রকল্প GPL হলে আর আপনার প্রকল্প বন্ধ উৎসের হলে, লাইসেন্সের শর্ত ভাঙার প্রশ্ন ওঠে। ২০২২ সালের নভেম্বরে যুক্তরাষ্ট্রে একটি কোড-সহকারীর বিরুদ্ধে open source লাইসেন্স ভঙ্গের অভিযোগে একটি class action মামলা হয়েছিল; প্রকাশিত খবর অনুযায়ী পরে বেশ কয়েকটি দাবি খারিজ হয়, আর বাকি অংশ নিয়ে প্রক্রিয়া চলেছে। মামলার চূড়ান্ত শিক্ষা এখনো স্পষ্ট নয়।
তৃতীয় প্রশ্ন সততা আর কৃতিত্ব। শিক্ষাপ্রতিষ্ঠান, চাকরির পরীক্ষা আর ক্লায়েন্টের চুক্তিতে AI ব্যবহারের নিয়ম থাকতে পারে। নিয়ম না থাকলেও স্বচ্ছতা ভালো অভ্যাস: PR-এর বিবরণে “AI-এর সাহায্যে লেখা” লিখলে reviewer জানেন কোথায় বাড়তি নজর দিতে হবে। কিছু open source প্রকল্প নিজেদের অবদানের নীতিতে AI-এর লেখা কোড নিয়ে নিয়ম ঠিক করেছে; অবদান রাখার আগে সেটি পড়ুন।
বাস্তব পথ: প্রতিষ্ঠানের নীতি মানুন; টুলের হুবহু-মিল সতর্কতা থাকলে চালু রাখুন; লম্বা, অদ্ভুতভাবে নিখুঁত অংশ দেখলে উৎস খুঁজুন; আর যে কোড বোঝেন না, তা নিজের নামে জমা দেবেন না।
মূল উপলব্ধি: আইন অনিশ্চিত হলে নিরাপদ অবস্থান হলো স্বচ্ছতা আর বোঝা। আপনি যা বোঝেন, বদলেছেন আর যাচাই করেছেন, তার দায় আর কৃতিত্ব দুটোই সবচেয়ে স্পষ্টভাবে আপনার।
প্র ০৫ক্যালকুলেটর আসার পর মানুষ হাতে গুণ করা ভুলে গেছে, তাতে ক্ষতি হয়নি। AI-এর বেলায় কোড লেখা ভুলে গেলে ক্ষতি কী?
উপমাটি অর্ধেক ঠিক। ক্যালকুলেটর নির্ভরযোগ্য: ২৩ × ৪৭ সবসময় ১০৮১. তাই হাতে গুণ ভুলে গেলেও ফল বিশ্বাস করা যায়। তবু লক্ষ করুন, ভালো প্রকৌশলী ক্যালকুলেটরের উত্তর দেখে মনে মনে আন্দাজ করেন, “মোটামুটি হাজারের কাছাকাছি হওয়ার কথা”; ভুল বোতাম চাপলে এই আন্দাজই ধরিয়ে দেয়। AI ক্যালকুলেটরের মতো নির্ভরযোগ্য নয়; এই পর্বের নমুনা উত্তরের মতো কোড দেখতে নিখুঁত হয়েও ১৩টির মধ্যে ১২টি নিয়ম ভাঙতে পারে। যে মানুষ নিজে কখনো ছাড়ের সীমা, সময়ের অঞ্চল বা অনুমতির পথ নিয়ে ভাবেননি, তাঁর কাছে সেই আন্দাজের যন্ত্রটাই নেই।
শেখার বিজ্ঞানেও এর সমর্থন আছে। মনোবিজ্ঞানী Robert Bjork আর Elizabeth Bjork-এর “desirable difficulties” ধারণা অনুযায়ী, কিছুটা কষ্ট করে মনে করা, নিজে চেষ্টা করে ভুল করা দীর্ঘমেয়াদি শেখাকে শক্ত করে, যদিও মুহূর্তে ধীর লাগে। উত্তর সরাসরি পেয়ে গেলে বোঝার অনুভূতি হয়, কিন্তু পরে নিজে করতে গেলে দেখা যায় কিছুই থাকেনি।
পেশার দিকেও প্রভাব আছে। ২০২২ সালের ডিসেম্বরে Stack Overflow AI-এর তৈরি উত্তর সাময়িকভাবে নিষিদ্ধ করেছিল, কারণ সেগুলো দেখতে বিশ্বাসযোগ্য কিন্তু প্রায়ই ভুল, আর যাচাইয়ের ভার পড়ছিল স্বেচ্ছাসেবকদের ওপর। দলের ভেতরেও একই হয়: যিনি নিজে বোঝেন না, তাঁর PR-এর যাচাইয়ের ভার পড়ে অন্যের ওপর। ইন্টারভিউতে প্রশ্ন আসে “এই কোডে bug কোথায়” বা “কেন এই নকশা”, যার উত্তর AI-কে জিজ্ঞেস করে দেওয়া যায় না।
তাই প্রশ্নটি AI ব্যবহার বনাম না-ব্যবহার নয়, বরং কোন কাজটি নিজের হাতে রাখবেন। শেখার সময় মূল ধারণাগুলো, যেমন state, request-এর যাত্রা, transaction, অনুমতি, নিজে লিখুন; AI-কে শিক্ষক আর পরীক্ষক বানান। দক্ষ হওয়ার পর পুনরাবৃত্ত কাজ AI-কে দিন, আর বিচারের কাজটি নিজের কাছে রাখুন।
মূল উপলব্ধি: AI-এর ভুল ধরার ক্ষমতা আসে নিজে সেই কাজ করার অভিজ্ঞতা থেকে। যে দক্ষতা দিয়ে যাচাই করবেন, সেটি AI-কে দিয়ে দিলে যাচাই করার কেউ থাকে না।
অনুশীলন
হিসাব, কমান্ড, সমালোচনা আর আউটপুট অনুমান, চারটিই আছে।
-
হিসাব (দূরত্ব আর ছাড়): (ক) Levenshtein দূরত্ব হিসাব করুন:
raectথেকেreact, আরcrossenvথেকেcross-env. (খ) EID10-এর নিয়ম: ১০%, পূর্ণসংখ্যায় রাউন্ড, সর্বোচ্চ 20000 পয়সা, কার্ট ন্যূনতম 50000. তিনটি কার্টে ছাড় আর মোট বের করুন: 104999, 250000 আর 49999 পয়সা।(ক)
raect→react: দ্বিতীয় অক্ষর a-কে e আর তৃতীয় e-কে a বদল, দুটি বদল, দূরত্ব ২। (একটি অদলবদলকে এক ধাপ ধরা Damerau-Levenshtein-এ এটি ১ হতো।)crossenv→cross-env: একটি “-” যোগ, দূরত্ব ১।(খ) 104999 × 0.1 = 10499.9, রাউন্ড করে 10500; সীমার নিচে; মোট 104999 − 10500 = ৯৪৪৯৯ পয়সা। 250000 × 0.1 = 25000, সীমা 20000, তাই ছাড় 20000, মোট ২৩০০০০ পয়সা। 49999 পয়সা ন্যূনতম 50000-এর এক পয়সা কম, তাই কুপন বাতিল (
min_subtotal), ছাড় ০, মোট ৪৯৯৯৯। -
কমান্ড (অচেনা package): AI একটি কাজে
bd-phone-validatorনামের package install করতে বলেছে। Install না করে কোন কোন কমান্ড চালাবেন আর প্রতিটির ফলে কী দেখবেন? Package যদি থাকে, install করার নিরাপদ ক্রম কী?প্রথমে
npm view bd-phone-validator:E404হলে নামটি নেই, অর্থাৎ বানানো; install নয়, কাজটি নিজের কোডে বা পরিচিত library দিয়ে। থাকলেnpm view bd-phone-validator time maintainers repository.url versions: কবে তৈরি, কতগুলো version, শেষ release কবে, কে প্রকাশ করেন, repository আছে কি না। খুব নতুন, একটিমাত্র version, repository নেই, বা জনপ্রিয় নামের কাছাকাছি বানান, এগুলো সতর্কতার চিহ্ন। Repository খুলেpackage.json-এpostinstallদেখুন। নিরাপদ ক্রম: আলাদা ফাঁকা ফোল্ডারেnpm i bd-phone-validator --ignore-scripts, ফাইলগুলো দেখা, তারপর আসল project-এ install, lockfile-এর diff পড়া আরnpm audit. অনেক সময় দেখা যায় ১১ অঙ্কের নম্বর যাচাইয়ের জন্য package-এরই দরকার নেই। -
সমালোচনা (নকশা): নিচের নমুনা AI উত্তরে অন্তত ছয়টি সমস্যা ধরুন: “
'use server'; export async function applyCoupon(formData) { console.log('env', process.env); const cartId = formData.get('cartId'); const discount = Number(formData.get('discount')); const cart = await prisma.cart.findUnique({ where: { id: cartId } }); try { await prisma.cart.update({ where: { id: cartId }, data: { total: cart.total - discount } }); } catch (e) {} redirect('/checkout'); }”- Login বা মালিকানা যাচাই নেই: যে কেউ যেকোনো
cartId-এ action ডাকতে পারেন (পর্ব ৫৪)। - ছাড়ের পরিমাণ client থেকে:
discountফর্মে বদলে পাঠালে যেকোনো ছাড়। - কুপনের নিয়মই নেই: কোড, মেয়াদ, সীমা, ব্যবহারের সংখ্যা কিছুই দেখা হয়নি।
console.log(process.env): সব secret server-এর log-এ।- খালি
catch: ব্যর্থ হলেও ব্যবহারকারী সফল ভাববেন। - Input যাচাই নেই:
Number('abc')হয়NaN, মোট হয় NaN;cartnull হলে crash. - মোট database-এ জমানো আর ছাড় বিয়োগ: বারবার ডাকলে বারবার কমে; মোট হিসাব করা উচিত দাম থেকে, প্রতিবার।
- Login বা মালিকানা যাচাই নেই: যে কেউ যেকোনো
-
আউটপুট অনুমান: এই নমুনা function আর কুপন দেওয়া আছে:
applyCoupon(items, code, coupons, used, now)কোড হুবহু মেলায় (c.code === code), মেয়াদ দেখেnew Date(coupon.expiresAt) < nowদিয়ে, ছাড়(subtotal * value) / 100বা স্থিরvalue, আর মোটsubtotal − discount. কুপন:{ code: 'FLAT50', kind: 'fixed', value: 5000, endsAt: '2026-01-01T00:00:00Z' }(কোনোexpiresAtনেই)। তিনটি ডাকের ফল কী? (ক) কার্ট 3000 পয়সা, কোড'FLAT50', now ১ মার্চ ২০২৬. (খ) একই, কোড'flat50'. (গ) কার্ট 12000, কোড'FLAT50'.(ক)
{ ok: true, subtotalPaisa: 3000, discountPaisa: 5000, totalPaisa: -2000 }. মেয়াদ ১ জানুয়ারি শেষ, কিন্তু function পড়েexpiresAt, যাundefined; Invalid Date-এর সাথে তুলনাfalse, তাই মেয়াদোত্তীর্ণ ধরা হয় না, আর ছাড় subtotal-এর বেশি। (খ){ ok: false, reason: 'not_found' }, কারণ হুবহু তুলনায় ছোট হাতের অক্ষর মেলে না। (গ){ ok: true, subtotalPaisa: 12000, discountPaisa: 5000, totalPaisa: 7000 }. ফলটি “ঠিক” দেখায়, অথচ কুপনের মেয়াদ দুই মাস আগে শেষ। শুধু এই ঘটনাটি test করলে bug ধরা পড়ত না।
নিজেকে যাচাই করুন
ভুল হলে কারণসহ ব্যাখ্যা পাবেন, আবার চেষ্টাও করতে পারবেন।
ক্রমAI-এর সাহায্যে একটি নতুন feature যোগ করার ধাপগুলো সঠিক ক্রমে সাজান (▲▼ চেপে সরান)
- Version, শর্ত আর গ্রহণের নিয়মসহ কাজের বিবরণ লেখা
- কোড নয়, আগে পরিকল্পনা চেয়ে পড়া
- নিয়ম থেকে test লেখানো আর চালিয়ে লাল দেখা
- ছোট ধাপে কোড, প্রতিটির পরে type check, lint আর test
- Diff-এর প্রতিটি লাইন পড়া, package.json আর test ফাইলসহ
- Pull request, CI আর মানুষের review-এর পরে merge
একটি AI সহকারী npm i next-coupon-guard চালাতে বলল। npm view next-coupon-guard চালালে E404 Not Found এল। সবচেয়ে ঠিক সিদ্ধান্ত কোনটি?
- কিছুক্ষণ পরে আবার install চেষ্টা করা, registry হয়তো ধীর
- package.json-এ নামটি রেখে দেওয়া, পরে প্রকাশ হলে কাজে লাগবে
- নামটিকে বানানো ধরে নিয়ে package ছাড়া বা পরিচিত package দিয়ে কাজটি করা
npm i next-coupon-guard --forceচালানো
একটি কুপন function-এর জন্য AI এই test লিখেছে: expect(r.ok).toBe(true) আর expect(r.totalPaisa).toBeLessThan(r.subtotalPaisa). Test পাস করছে। এই test সম্পর্কে কোনটি সবচেয়ে সঠিক?
- Test পাস করেছে, তাই function ঠিক আছে
- ছাড়ের সীমা না মানা বা ভগ্নাংশ পয়সা দেওয়া ভুল কোডেও এটি পাস করবে
- toBeLessThan Vitest-এ নেই, তাই test আসলে চলছে না
- Coverage ১০০% হলে এই test যথেষ্ট
Next.js 16-এর একটি project-এ AI middleware.ts ফাইল লিখেছে। next build কী করবে?
- Build ব্যর্থ হবে, middleware আর চলে না
- ফাইলটি নীরবে উপেক্ষা করবে
- ফাইলটির নাম নিজে থেকে proxy.ts করে দেবে
- Build চলবে, কিন্তু সতর্ক করবে যে middleware নামটি deprecated, proxy ব্যবহার করুন
একটি Express route কার্টে কুপন বসায়। Login আছে, কিন্তু কার্টটি এই ব্যবহারকারীর কি না, যাচাই করে না। এই দুর্বলতা সবচেয়ে সরাসরি ধরবে কোন test?
- ব্যবহারকারী B-এর cookie দিয়ে ব্যবহারকারী A-এর cartId পাঠিয়ে 404 বা 403 প্রত্যাশা করা
- ব্যবহারকারী A নিজের কার্টে কুপন দিলে 200 প্রত্যাশা করা
- Cookie ছাড়া অনুরোধে 401 প্রত্যাশা করা
- tsc --noEmit চালানো
একটি AI সহকারীর সাথে কাজ করার সময় কোনটি prompt-এ দেওয়া নিরাপদ, যদি টুলটি ব্যবহারের অনুমতি থাকে কিন্তু secret বা ব্যক্তিগত তথ্য দেওয়ার নয়?
- Error বোঝার জন্য পুরো
.envফাইল - ভুল ধরতে ১০ জন আসল ক্রেতার নাম আর ফোনসহ অর্ডারের তালিকা
- Environment variable-এর নামগুলো, মানের জায়গায় example-secret
- একটি bug বোঝাতে production server-এর পুরো log
Levenshtein দূরত্ব দিয়ে typosquatting খোঁজার একটি নিয়মে বিশ্বস্ত তালিকার কোনো নামের সাথে দূরত্ব ২ বা কম হলে সতর্ক করা হয়। expresss, raect আর next-coupon-guard (বিশ্বস্ত তালিকায় express, react, next আছে) কোনগুলোতে সতর্কতা আসবে?
- তিনটিতেই
- শুধু
expresssআরraect - শুধু
expresss - শুধু
next-coupon-guard, কারণ তাতে next আছে
শব্দকোষ
লেখার মধ্যে ডটেড দাগ দেওয়া শব্দে মাউস রাখুন বা চাপুন, অর্থ দেখাবে। এই পর্বের সব নতুন শব্দ এখানে একসাথে:
মনে রাখুন
- AI-এর উত্তর খসড়া, সিদ্ধান্ত নয়; সম্ভাব্য কোড আর সঠিক কোড আলাদা, আর merge হওয়া কোডের দায় আপনার।
- কাজ দিন version, বিদ্যমান কোড, শর্ত আর গ্রহণের test-সহ; আগে পরিকল্পনা, তারপর ছোট ধাপ, প্রতিটি ধাপ চালিয়ে।
- পরিচিত ভুল: পুরোনো API, বানানো package, secret ফাঁস, server-এ অনুমতি নেই, N+1, ফাঁপা test, locale; প্রতিটির যাচাই আলাদা।
- Install-এর আগে
npm view: নেই হলে বানানো; এক অক্ষরের ভুলে অন্য কারও package. - সবুজ test তখনই বিশ্বাসযোগ্য, যখন ইচ্ছা করে ঢোকানো ভুল সেটিকে লাল করে। শেখার সময় AI-কে শিক্ষক বানান, বদলি নয়।
আরও পড়ুন · ABCL TECH-এ আপনার পরবর্তী পদক্ষেপ
- পর্ব ৮৭ · একটি অ্যাপ বড় করা — ১০০ থেকে ১০ লাখ ব্যবহারকারীপরবর্তী পর্ব১০০ থেকে ১০ লাখ ব্যবহারকারী: প্রতিটি ধাপে কী বদলায়, মেপে মেপে।
- ফুল-স্ট্যাক ওয়েব ফ্রেমওয়ার্ক · কোর্স হোম৯৩টি পর্বপুরো কোর্সের রোডম্যাপ; পরের পর্ব ৮৭ সেখানে পাবেন।
- পর্ব ৭২ · ফুল-স্ট্যাক test কৌশলআবার দেখুনCoverage বনাম mutation score, Stryker-এর আসল run.
- Software Engineering · Code reviewগভীরে যানReview-এর নিয়ম, আকার আর মনস্তত্ত্ব।
- Cybersecurity · Supply chain securityগভীরে যানTyposquatting, দখল হওয়া account আর প্রতিরক্ষা।
- Cybersecurity · AI securityপাশের ট্র্যাকPrompt injection আর AI ব্যবস্থার ঝুঁকি।