Error Handling & Debugging Like a Pro
"Building Bulletproof Applications"
ভাবো তো, তুমি ফেসবুকে একটা পোস্ট করছো, হঠাৎ ইন্টারনেট চলে গেল আর পুরো অ্যাপ ক্র্যাশ করে বন্ধ হয়ে গেল। তুমি কি খুশি হবে? একদমই না।
সফটওয়্যার ডেভেলপমেন্টে এরর আসবেই (Network Fail, Server Down, Bad Input)। কিন্তু অ্যাপ যাতে ক্র্যাশ না করে, ইউজার যেন একটা সুন্দর মেসেজ পায়—সেটাই হলো Error Handling।
এই অধ্যায়ে আমরা শিখব কিভাবে কোডকে Defensive (আত্মরক্ষামূলক) এবং Robust (শক্তপোক্ত) বানাতে হয়।
⭐ ১. Error আসলে কী? (The Crash Report)
জাভাস্ক্রিপ্টে এরর হলো একটি অবজেক্ট যা বলে দেয় "কোথায় এবং কেন" সমস্যা হয়েছে। কিছু বিল্ট-ইন এরর আছে যা চিনে রাখা জরুরি:
- ReferenceError: এমন ভেরিয়েবল ডাকছো যা অস্তিত্বই নেই। (x is not defined)
- TypeError: ভাতের চামচ দিয়ে রুটি কাটার চেষ্টা। (null.filter is not a function)
- SyntaxError: কোড লিখতেই ভুল করেছো। (Unexpected token })
- NetworkError: ফেচ ফেইল করলে
⭐ ২. The Safety Net: try...catch...finally 🛡️
সবচেয়ে বেসিক এবং শক্তিশালী টুল।
Structure
try {
// ১. রিস্কি কোড এখানে লিখবে (যেমন: JSON parsing, API call)
const user = JSON.parse("Invalid JSON String");
} catch (error) {
// ২. এরর হলে কোড ক্র্যাশ না করে এখানে আসবে
console.log("Error caught:", error.message);
// ইউজারকে সুন্দর মেসেজ দেখাও: "Something went wrong!"
} finally {
// ৩. এরর হোক বা না হোক, এটা রান হবেই
console.log("Cleaning up... Loading spinner stopped.");
}
Senior Insight (finally কেন দরকার?):
ধরো তুমি লোডিং স্পিনার চালু করে API কল করলে। এরর হলে catch-এ গেলে, কিন্তু স্পিনার বন্ধ করতে ভুলে গেলে। finally ব্লক ব্যবহার করলে এরর হোক বা সাকসেস, স্পিনার বন্ধ হবেই।
⭐ ৩. Throwing Custom Errors (Be the Police 👮♂️)
মাঝেমধ্যে কোড ঠিকঠাক রান করলেও লজিক্যালি সেটা ভুল হতে পারে। তখন আমরা নিজেরাই এরর throw করি।
function withdrawMoney(amount, balance) {
if (amount < 0) {
throw new Error("টাকার পরিমাণ নেগেটিভ হতে পারে না!");
}
if (amount > balance) {
throw new Error("অপর্যাপ্ত ব্যালেন্স!");
}
return balance - amount;
}
// ব্যবহার
try {
withdrawMoney(500, 100);
} catch (e) {
console.error("Alert:", e.message); // Alert: অপর্যাপ্ত ব্যালেন্স!
}
⭐ ৪. Custom Error Classes (Senior Level Architecture)
বড় প্রজেক্টে সব এররকে শুধু Error বললে বোঝা যায় না কোনটা সার্ভার এরর আর কোনটা ভ্যালিডেশন এরর। তাই আমরা নিজস্ব এরর ক্লাস বানাই।
class ValidationError extends Error {
constructor(message) {
super(message);
this.name = "ValidationError"; // নাম সেট করলাম
}
}
// ব্যবহার
function login(email) {
if (!email.includes("@")) {
throw new ValidationError("ইমেইল ঠিক নেই!");
}
}
এতে সুবিধা হলো, catch ব্লকে তুমি চেক করতে পারবে এররটা কোন টাইপের এবং সেই অনুযায়ী ব্যবস্থা নিতে পারবে।
⭐ ৫. Defensive Coding (প্রতিকারের চেয়ে প্রতিরোধ ভালো)
এরর হ্যান্ডল করার চেয়ে এরর যাতে না হয় সেই ব্যবস্থা করা ভালো।
A) Optional Chaining (?.)
নেস্টেড অবজেক্ট এক্সেস করতে গিয়ে undefined এরর খাওয়া থেকে বাঁচায়।
const user = {}; // address নেই
// ❌ Crash করবে
// console.log(user.address.city);
// ✅ Safe (undefined দেবে, ক্র্যাশ করবে না)
console.log(user?.address?.city);
B) Nullish Coalescing (??)
ডিফল্ট ভ্যালু সেট করার জন্য।
const input = null;
const value = input ?? "Default Value"; // input null/undefined হলেই কেবল ডিফল্ট নেবে
C) Safe JSON Parse (Utility Pattern)
JSON.parse যেকোনো সময় ক্র্যাশ করতে পারে। তাই সিনিয়ররা র্যাপার ফাংশন ব্যবহার করে।
function safeJsonParse(str) {
try {
return JSON.parse(str);
} catch {
return null; // ক্র্যাশ না করে নাল রিটার্ন করো
}
}
⭐ ৬. Async Error Handling (Promises & Await)
এখানেই জুনিয়ররা ধরা খায়। await ব্যবহার করলে অবশ্যই try-catch লাগবে।
async function getData() {
try {
const res = await fetch("/api");
const data = await res.json();
} catch (error) {
// ইন্টারনেট না থাকলে বা সার্ভার ডাউন থাকলে এখানে আসবে
console.log("Network Error:", error.message);
}
}
Global Safety Net (শেষ রক্ষা)
যদি কোনো প্রমিজ catch করতে ভুলে যাও, তবে পুরো অ্যাপ যাতে ক্র্যাশ না করে, তার জন্য গ্লোবাল হ্যান্ডলার রাখা উচিত।
// Browser
window.addEventListener("unhandledrejection", (event) => {
console.error("Uncaught Promise Error:", event.reason);
// এখানে তুমি এররটা সার্ভারে (Sentry/LogRocket) পাঠিয়ে দিতে পারো
});
⭐ ৭. Advanced Patterns (Senior Must-Know)
🔥 Pattern 1: Retry Logic
নেটওয়ার্ক ফ্লিকার করলে বা সার্ভার বিজি থাকলে সাথে সাথে এরর না দিয়ে ৩ বার চেষ্টা করো।
async function fetchWithRetry(url, retries = 3) {
try {
return await fetch(url);
} catch (err) {
if (retries <= 1) throw err; // আর সুযোগ নেই
return fetchWithRetry(url, retries - 1); // আবার চেষ্টা
}
}
🔥 Pattern 2: Circuit Breaker (ফিউজ বক্স)
যদি পর পর ৫ বার API ফেইল করে, তবে ৬ষ্ঠ বার আর কলই করো না। কিছুক্ষণ ব্রেক নাও। এটা সিস্টেমকে লোড থেকে বাঁচায়।
let failures = 0;
const threshold = 5;
async function safeApiCall() {
if (failures >= threshold) throw new Error("Service is down! Try later.");
try {
await fetch("/api");
failures = 0; // সফল হলে রিসেট
} catch (e) {
failures++;
throw e;
}
}
🧠 ৮. Senior Interview Questions
Q1: throw new Error() এবং return new Error() এর মধ্যে পার্থক্য কী?
উত্তর: throw করলে ফাংশন সেখানেই থেমে যায় এবং কন্ট্রোল catch ব্লকে চলে যায় (Execution stops)। কিন্তু return করলে সেটা নরমাল ভ্যালু হিসেবে রিটার্ন হয়, ফাংশন থামে না বা এরর হিসেবে গণ্য হয় না যদি না রিসিভার সেটা চেক করে।
Q2: Defensive Programming কী?
উত্তর: এমনভাবে কোড লেখা যাতে সম্ভাব্য এররগুলো আগেই চেক করা হয়। যেমন: if (!user) return; বা অপশনাল চেইনিং user?.name ব্যবহার করা, যাতে অ্যাপ ক্র্যাশ না করে।
Q3: Production এ console.log বা console.error কি যথেষ্ট?
উত্তর: না। ইউজারের ব্রাউজারে কনসোল লগ করে লাভ নেই, ডেভেলপার সেটা দেখতে পায় না। প্রোডাকশনে Sentry, Datadog বা LogRocket এর মতো টুল ব্যবহার করে এররগুলো রিমোট সার্ভারে পাঠাতে হয়।
🚀 অধ্যায় ১৫ সারাংশ (Checklist)
আমরা শিখলাম:
- ✓ Basic: try-catch-finally এর সঠিক ব্যবহার
- ✓ Logic: throw ব্যবহার করে কাস্টম ভ্যালিডেশন আটকানো
- ✓ Safety: ?. এবং ?? ব্যবহার করে ক্র্যাশ ঠেকানো
- ✓ Async: unhandledrejection দিয়ে ভুলে যাওয়া প্রমিজ এরর হ্যান্ডল করা
- ✓ Patterns: Retry এবং Circuit Breaker এর কনসেপ্ট
Next Step: আমাদের কোড এখন বুলেটপ্রুফ। কিন্তু কোডগুলো মেইনটেইন করা কঠিন হয়ে যাচ্ছে কারণ সব এক ফাইলে। এখন আমাদের কোডকে মডিউলে ভাগ করতে হবে। পরের অধ্যায়: "Modules & Bundlers (Import/Export)"।