Exception handling không phải nghệ thuật làm cho error biến mất. Mục tiêu là giữ invariant, trả response phù hợp và để lại đủ context cho người trực production—thường là chính ta, nhưng ít ngủ hơn.
Rescue exception cụ thể
1 | begin |
Tránh:
1 | rescue Exception |
Exception bao gồm tín hiệu hệ thống như SystemExit, Interrupt, NoMemoryError. Thông thường chỉ rescue subclass của StandardError, tốt nhất là class cụ thể bạn thật sự xử lý được.
rescue_from ở controller boundary
1 | class ApplicationController < ActionController::Base |
Đây là boundary tốt để map domain/framework exception sang HTTP. Không biến mọi exception thành 200 OK kèm { success: false }; status code tồn tại vì client cũng có cảm xúc.
Service object và transaction
1 | class TransferMoney |
Raise khi invariant bị vi phạm; transaction rollback. Chỉ rescue ở nơi có quyết định meaningful: retry, translate, compensate hoặc trả response.
Log có context, không log secret
1 | Rails.logger.error( |
APM/error tracker nên giữ stack trace và request correlation ID. Không log password, token, full payment data hoặc nguyên params.
Background jobs
Retry chỉ phù hợp với lỗi tạm thời. Validation failure hay record không tồn tại thường không tự lành sau 17 lần retry.
Job có side effect nên idempotent hoặc có deduplication. Nếu lần đầu đã charge tiền nhưng response mất, retry mù là cách biến reliability thành chương trình khách hàng thân thiết.
ensure
Dùng ensure để cleanup resource:
1 | file = File.open(path) |
Ưu tiên block API khi có thể vì Ruby tự cleanup:
1 | File.open(path) { |file| process(file) } |
Checklist
- Rescue class cụ thể.
- Không nuốt error rồi trả dữ liệu giả.
- Giữ transaction/invariant.
- Retry có giới hạn và chỉ cho lỗi transient.
- Log context, không log secret.
- Map exception sang HTTP ở boundary.
- Để error không xử lý được tiếp tục raise tới monitoring.