DDevArchive
Đăng nhập

Error handling: khi mọi thứ có thể sai, SẼ sai

Mạng rớt. API trả 500. User nhập emoji vào ô số điện thoại. Database timeout. File upload bị corrupt. Nếu bạn không xử lý lỗi → app crash → user bỏ đi. Bài này dạy cách code "defensive" — chuẩn bị cho mọi thứ sai.

Murphy’s Law: cái gì sai được, sẽ sai

Junior developer code cho “happy path” — khi mọi thứ đúng. Senior developer code cho unhappy path — khi mọi thứ sai. 80% thời gian debug là xử lý case bạn không nghĩ tới.

Pattern 1: try/catch đúng cách

// File: error-handling.ts
// ❌ SAI: catch rồi nuốt lỗi
try {
  await saveUser(data)
} catch (err) {
  console.log(err) // Rồi sao? User thấy gì?
}

// ✅ ĐÚNG: catch → log → báo user → recover
try {
  await saveUser(data)
} catch (err) {
  // 1. Log chi tiết cho developer debug
  logger.error("Failed to save user", {
    userId: data.id,
    error: err.message,
    stack: err.stack,
  })

  // 2. Báo user message thân thiện (KHÔNG lộ stack trace)
  throw new AppError(
    "Không thể lưu thông tin. Vui lòng thử lại.",
    500
  )
}

Pattern 2: React Error Boundary

// File: ErrorBoundary.tsx
// Component lỗi → không crash cả app
import { Component, ReactNode } from 'react'

interface Props { children: ReactNode; fallback: ReactNode }
interface State { hasError: boolean }

class ErrorBoundary extends Component<Props, State> {
  state = { hasError: false }

  static getDerivedStateFromError() {
    return { hasError: true }
  }

  componentDidCatch(error: Error, info: React.ErrorInfo) {
    // Gửi lên Sentry/monitoring
    captureException(error, { extra: info })
  }

  render() {
    if (this.state.hasError) return this.props.fallback
    return this.props.children
  }
}

// Sử dụng: component Dashboard crash → chỉ Dashboard lỗi, app vẫn chạy
// <ErrorBoundary fallback={<p>Có lỗi xảy ra</p>}>
//   <Dashboard />
// </ErrorBoundary>

Pattern 3: Graceful degradation

Tình huốngCrash (Bad)Graceful (Good)
API trả 500Trang trắngHiện data cũ từ cache + banner “Đang cập nhật”
Ảnh load thất bạiBroken image iconPlaceholder image + retry
Payment timeout“Error 500”“Thanh toán đang xử lý, chúng tôi sẽ email xác nhận trong 5 phút”
Search trả 0 kết quảTrang trống“Không tìm thấy X. Thử tìm Y?”
KHÔNG BAO GIỜ lộ stack trace cho user

res.status(500).json({ error: err.stack }) → TUYỆT ĐỐI KHÔNG. Stack trace chứa: đường dẫn server, tên file, library version → hacker dùng để tấn công. User chỉ cần thấy “Có lỗi xảy ra, vui lòng thử lại.”

❓ Khi catch lỗi, điều QUAN TRỌNG NHẤT là gì?

  • Wrap async code trong try/catch
  • Tạo class AppError với message thân thiện
  • Setup React Error Boundary cho component
  • Không lộ stack trace trong API response