Giao diện
Lỗi thường gặp ⚠️
Lỗi là bạn của bạn. Chúng đang cố nói cho bạn điều gì đó. Hãy lắng nghe.
Tip 1: Reading Stack Traces
Problem
Bức tường chữ đỏ xuất hiện, bạn panic và không biết đọc từ đâu.
Solution
Đọc từ dưới lên: Framework code ở trên, YOUR code ở dưới.
Example
TypeError: Cannot read property 'name' of undefined
at Object.get (node_modules/express/lib/request.js:345)
at Layer.handle (node_modules/express/lib/router/layer.js:95)
at trim_prefix (node_modules/express/lib/router/index.js:317)
at Router.handle (node_modules/express/lib/router/index.js:284)
at Function.handle (node_modules/express/lib/application.js:644)
at getUserProfile (src/controllers/user.js:23) ← START HERE!
at processRequest (src/middleware/auth.js:45)Reading Strategy:
- Đọc error type:
TypeError - Đọc message:
Cannot read property 'name' of undefined - Scroll xuống tìm YOUR code:
src/controllers/user.js:23 - Mở file đó, line 23
- Fix: Variable nào là undefined?
Pro Tips
- Framework/library code (node_modules): Bỏ qua
- YOUR code: Focus vào đây
- Multiple YOUR files: Start từ file đầu tiên
- Line numbers: Exact location
Tip 2: "Works on My Machine"
Problem
Code chạy được trên laptop bạn, nhưng fail trên production hoặc laptop đồng nghiệp.
Solution
Environment differences: Node version, OS, environment variables, database state.
Example
javascript
// Bad: Hardcoded paths (Windows vs Linux)
const filePath = 'C:\\Users\\me\\data.json'; // ❌ Chỉ chạy trên Windows
// Good: Cross-platform paths
const path = require('path');
const filePath = path.join(__dirname, 'data.json'); // ✅ Chạy mọi nơi
// Bad: Missing environment variables
const apiKey = 'hardcoded-key'; // ❌ Không secure, không flexible
// Good: Environment variables
const apiKey = process.env.API_KEY; // ✅ Config per environment
if (!apiKey) {
throw new Error('API_KEY environment variable is required');
}Pro Tips
- Dockerize: Ship environment, không chỉ code
- Environment variables: Dùng
.envfiles - Version lock:
package-lock.json,yarn.lock - Document dependencies: README với setup instructions
Tip 3: Null/Undefined Errors
Problem
Cannot read property 'x' of undefined - lỗi phổ biến nhất trong JavaScript.
Solution
Defensive programming: Validate inputs, use optional chaining.
Example
javascript
// Bad: Assume data exists
function displayUser(user) {
return user.profile.name; // ❌ Crashes if user or profile is null
}
// Good: Optional chaining (ES2020+)
function displayUser(user) {
return user?.profile?.name ?? 'Unknown'; // ✅ Safe
}
// Good: Explicit validation
function displayUser(user) {
if (!user || !user.profile) {
return 'Unknown';
}
return user.profile.name;
}
// Good: TypeScript
function displayUser(user: User | null): string {
return user?.profile?.name ?? 'Unknown';
}Pro Tips
- Optional chaining:
obj?.prop?.nested - Nullish coalescing:
value ?? defaultValue - TypeScript: Catch at compile time
- Validate at boundaries: API responses, user inputs
Tip 4: Async/Await Errors
Problem
Async code không chạy đúng thứ tự, hoặc errors không được catch.
Solution
Always await promises, wrap trong try-catch.
Example
javascript
// Bad: Forgot await
async function getUser(id) {
const user = fetchUser(id); // ❌ Returns Promise, not user
console.log(user.name); // ❌ undefined
}
// Good: Await promise
async function getUser(id) {
const user = await fetchUser(id); // ✅ Wait for result
console.log(user.name);
}
// Bad: No error handling
async function getUser(id) {
const user = await fetchUser(id); // ❌ If fails, crashes app
return user;
}
// Good: Try-catch
async function getUser(id) {
try {
const user = await fetchUser(id);
return user;
} catch (error) {
console.error('Failed to fetch user:', error);
throw new Error(`User ${id} not found`);
}
}Pro Tips
- ESLint rule:
no-floating-promises - Always return from async functions
- Parallel requests:
Promise.all([req1, req2]) - Sequential:
awaiteach one
Tip 5: Off-by-One Errors
Problem
Loop chạy quá ít hoặc quá nhiều lần, array index out of bounds.
Solution
Careful với boundary conditions: < vs <=, 0 vs 1 indexed.
Example
javascript
// Bad: Off-by-one
const arr = [1, 2, 3, 4, 5];
for (let i = 1; i <= arr.length; i++) { // ❌ Starts at 1, goes to 6
console.log(arr[i]); // Misses first, undefined at end
}
// Good: Correct boundaries
for (let i = 0; i < arr.length; i++) { // ✅ 0 to 4
console.log(arr[i]);
}
// Better: Use array methods
arr.forEach(item => console.log(item));
// Bad: Slice off-by-one
const last3 = arr.slice(-4); // ❌ Gets 4 items, not 3
// Good: Correct slice
const last3 = arr.slice(-3); // ✅ Gets last 3 itemsPro Tips
- Test boundary conditions: Empty array, single item, full array
- Use array methods:
forEach,map,filter(no index management) - Inclusive vs exclusive:
[start, end)in most languages
Tip 6: Type Coercion Bugs
Problem
JavaScript type coercion gây ra unexpected behavior.
Solution
Use strict equality (===), explicit type conversion.
Example
javascript
// Bad: Loose equality
if (user.age == '18') { // ❌ True for both 18 and '18'
console.log('Adult');
}
// Good: Strict equality
if (user.age === 18) { // ✅ Only true for number 18
console.log('Adult');
}
// Bad: Truthy/falsy confusion
if (user.count) { // ❌ False for 0, but 0 is valid count
console.log('Has items');
}
// Good: Explicit check
if (user.count > 0) { // ✅ Clear intent
console.log('Has items');
}
// Bad: String concatenation
const total = '10' + 5; // ❌ '105' (string)
// Good: Explicit conversion
const total = Number('10') + 5; // ✅ 15 (number)Pro Tips
- Always use
===and!== - ESLint rule:
eqeqeq - TypeScript: Catch type errors at compile time
- Be explicit:
Number(),String(),Boolean()
Tip 7: Mutation Bugs
Problem
Accidentally mutating objects/arrays gây side effects không mong muốn.
Solution
Immutable updates: Tạo copy thay vì mutate original.
Example
javascript
// Bad: Mutate original
function addItem(cart, item) {
cart.items.push(item); // ❌ Mutates original cart
return cart;
}
// Good: Immutable update
function addItem(cart, item) {
return {
...cart,
items: [...cart.items, item] // ✅ New array
};
}
// Bad: Shallow copy trap
const user = { name: 'John', address: { city: 'NYC' } };
const copy = { ...user };
copy.address.city = 'LA'; // ❌ Mutates original user.address!
// Good: Deep copy
const copy = JSON.parse(JSON.stringify(user)); // ✅ True deep copy
// Or use lodash: _.cloneDeep(user)Pro Tips
- Spread operator:
[...arr],{...obj}(shallow copy) - Array methods:
map,filter,slice(return new array) - Avoid:
push,pop,splice,sort(mutate original) - Immutable libraries: Immer, Immutable.js
Tip 8: Race Conditions
Problem
Multiple async operations chạy đồng thời, kết quả phụ thuộc vào timing.
Solution
Serialize operations hoặc use proper synchronization.
Example
javascript
// Bad: Race condition
let counter = 0;
async function increment() {
const current = counter;
await delay(100); // Simulate async work
counter = current + 1; // ❌ Lost updates if called concurrently
}
// Good: Atomic operation
let counter = 0;
async function increment() {
counter++; // ✅ Atomic (but still not thread-safe in multi-threaded)
}
// Bad: Stale closure
for (const i = 0; i < 5; i++) {
setTimeout(() => console.log(i), 100); // ❌ Prints 5, 5, 5, 5, 5
}
// Good: Block scope
for (let i = 0; i < 5; i++) {
setTimeout(() => console.log(i), 100); // ✅ Prints 0, 1, 2, 3, 4
}Pro Tips
- Use
letinstead ofvar - Locks/mutexes for critical sections
- Queue async operations:
p-queuelibrary - Test with concurrent requests
Tip 9: Memory Leaks
Problem
Application memory usage tăng dần, eventually crashes.
Solution
Clean up: Remove event listeners, clear timers, close connections.
Example
javascript
// Bad: Memory leak
function setupListener() {
const button = document.getElementById('btn');
button.addEventListener('click', handleClick); // ❌ Never removed
}
// Good: Cleanup
function setupListener() {
const button = document.getElementById('btn');
const handler = () => handleClick();
button.addEventListener('click', handler);
// Cleanup function
return () => button.removeEventListener('click', handler);
}
// Bad: Timer leak
function startPolling() {
setInterval(() => fetchData(), 1000); // ❌ Never cleared
}
// Good: Clear timer
function startPolling() {
const intervalId = setInterval(() => fetchData(), 1000);
// Cleanup function
return () => clearInterval(intervalId);
}
// React example
useEffect(() => {
const cleanup = setupListener();
return cleanup; // ✅ Cleanup on unmount
}, []);Pro Tips
- Browser DevTools: Memory profiler
- Node.js:
--inspectflag, Chrome DevTools - Common causes: Event listeners, timers, closures, global variables
- Test: Run app for extended period, monitor memory
Tip 10: CORS Errors
Problem
Access to fetch at 'https://api.example.com' from origin 'http://localhost:3000' has been blocked by CORS policy
Solution
Configure CORS headers on server, or use proxy in development.
Example
javascript
// Backend: Enable CORS (Express)
const cors = require('cors');
app.use(cors({
origin: 'http://localhost:3000', // Allow specific origin
credentials: true // Allow cookies
}));
// Or allow all (development only!)
app.use(cors());
// Frontend: Include credentials
fetch('https://api.example.com/data', {
credentials: 'include' // Send cookies
});
// Development proxy (package.json)
{
"proxy": "http://localhost:5000"
}
// Now fetch from relative URL
fetch('/api/data'); // Proxied to http://localhost:5000/api/dataPro Tips
- CORS is browser security, not server security
- Production: Whitelist specific origins
- Development: Use proxy to avoid CORS
- Preflight requests: OPTIONS method for complex requests
📋 Quick Reference
| Error | Common Cause | Quick Fix |
|---|---|---|
| Stack Trace | Framework code | Read from bottom up |
| Works on My Machine | Environment differences | Docker, env vars |
| Null/Undefined | Missing validation | Optional chaining ?. |
| Async Errors | Forgot await | Add await, try-catch |
| Off-by-One | Wrong boundaries | Test edge cases |
| Type Coercion | Loose equality | Use === |
| Mutation | Mutating objects | Immutable updates |
| Race Conditions | Concurrent async | Serialize operations |
| Memory Leaks | No cleanup | Remove listeners, clear timers |
| CORS | Missing headers | Enable CORS on server |