0

[Series Phỏng Vấn Backend] #3: Trả Lời Câu Hỏi "Kể Về Một Con Bug Khó Nhất Bạn Từng Gặp?"

Chào mừng anh em quay trở lại với series Giải Mã Phỏng Vấn Backend.

Trong bất kỳ buổi phỏng vấn Mid/Senior nào, chắc chắn bạn sẽ gặp một câu hỏi kinh điển: "Em hãy kể anh nghe về một lần em gặp một con bug khó. Cụ thể là code chạy ngon trên máy local nhưng lên môi trường test/production thì lại lỗi. Em đã phát hiện ra sao, lần theo nó thế nào, và cuối cùng nguyên nhân gốc là gì?"

Nhà tuyển dụng hỏi câu này không phải để nghe xem con bug đó có "khủng" hay không. Cái họ muốn nghe là cách bạn suy nghĩ, cách bạn khoanh vùng vấn đề và thái độ của bạn khi đối mặt với áp lực.

Hôm nay, mình sẽ chia sẻ một "kỷ niệm đau thương" thực tế với Laravel để làm kịch bản mẫu cho câu hỏi này. Một kịch bản hội tụ đủ: Triệu chứng \rightarrow Cách lần vết \rightarrow Nguyên nhân \rightarrow Cách Fix \rightarrow Bài học.

1. Triệu Chứng (Symptoms): Lời Nguyền "Trên Máy Em Chạy Bình Thường"

Hồi đó, mình được giao làm tính năng tích hợp API với một bên thứ 3 (Cổng thanh toán). Khi code và test trên môi trường Local, mọi request đều trả về HTTP 200 OK, luồng chạy cực kỳ mượt mà.

Nhưng ngay sau khi merge code và deploy lên môi trường Production, tính năng đó... chết đứng. Hệ thống liên tục văng ra lỗi HTTP 401 Unauthorized từ phía đối tác, khiến user không thể hoàn tất giao dịch.

2. Truy Vết (Tracing): Bình Tĩnh Khoanh Vùng, Không Đoán Mò

Khi nhận được bug trên Prod, quy tắc số 1 là không được hoảng loạn sửa code mù quáng. Mình bắt đầu lần theo dấu vết:

  • Bước 1: Soi Log. Chui vào storage/logs/laravel.log trên server mình check chi tiết lỗi 401. Khi dump thử payload gửi đi, mình phát hiện ra chuỗi API Key đính kèm trong Header đang bị rỗng (null).
  • Bước 2: Kiểm tra môi trường (Environment). Suy nghĩ đầu tiên nảy lên: "Chắc lúc deploy, anh em DevOps quên cấu hình biến môi trường này rồi". Mình lập tức nhờ check lại file .env trên Production. Nhưng bất ngờ thay, file .env vẫn nằm đó, API Key được điền đầy đủ và hoàn toàn chính xác.
  • Bước 3: Tái hiện (Reproduce). Cùng một file .env, sao code lại không nhận? Mình xin phép dùng php artisan tinker trên môi trường Staging (có setup giống hệt Prod) để test thử. Khi gõ lệnh env('THIRD_PARTY_API_KEY'), kết quả trả về đúng là null thật!

Lúc này, mình lờ mờ nhận ra vấn đề không nằm ở DevOps, mà nằm ở cơ chế của Framework.

3. Nguyên Nhân Gốc Rễ (Root Cause): Khoảnh Khắc "À Há!"

Bình tĩnh xâu chuỗi lại, mình nhớ ra sự khác biệt lớn nhất giữa Local và Production trong Laravel chính là Cơ chế Caching.

Trên Local, chúng ta thường không cache config, nên hàm env() gọi ở đâu cũng lấy được dữ liệu trực tiếp từ file .env. Tuy nhiên, trong pipeline CI/CD deploy lên Production, hệ thống LUÔN chạy lệnh php artisan config:cache để gộp toàn bộ file config lại thành một file duy nhất nhằm tối ưu tốc độ boot của framework.

Và đây là "cú lừa" được ghi rành rành trong Document của Laravel nhưng nhiều anh em bỏ qua: Một khi cấu hình đã được cache, hàm env() nếu được gọi ở bất cứ đâu bên ngoài thư mục config/ sẽ lập tức trả về null. Trong code của mình lúc đó, mình lại lười và gọi trực tiếp env('THIRD_PARTY_API_KEY') ngay bên trong file PaymentService.php. Đó chính là nguyên nhân gốc rễ!

4. Cách Khắc Phục (The Fix): Gọn Gàng Và Chuẩn Chỉ

Giải pháp fix thì rất nhanh và chỉ tốn 2 phút:

  1. Mình khai báo biến đó vào trong file config/services.php: 'payment_key' => env('THIRD_PARTY_API_KEY')
  2. Trong PaymentService.php, mình thay thế dòng code cũ thành: $apiKey = config('services.payment_key');

Sau đó push code, chạy lại php artisan config:clearconfig:cache trên Prod. Hệ thống lập tức chạy trơn tru, API Key được nhận đầy đủ và lỗi 401 biến mất.

5. Bài Học Rút Ra (The Lesson): Trưởng Thành Từ Những Cú Vấp

Câu trả lời phỏng vấn sẽ không trọn vẹn nếu thiếu đi phần đúc kết. Sau case đó, mình rút ra được 2 nguyên tắc sống còn:

  • Về Coding Convention: Tuyệt đối không bao giờ lạm dụng helper env() rải rác trong các logic code (Controller, Service, Model...). Mọi biến môi trường bắt buộc phải được quy tụ về thư mục config/ và gọi ra qua helper config().
  • Về Tư duy Hệ thống: Phải nắm rất vững vòng đời (Lifecycle) và các cơ chế Optimize của framework ở môi trường Production. Môi trường Dev quá thoải mái đôi khi sẽ che giấu đi những sai sót chí mạng về mặt kiến trúc.

Tổng Kết

Một câu chuyện debug hay không nằm ở việc lỗi đó cao siêu đến mức nào, mà nằm ở việc bạn thể hiện được sự logic, mạch lạc trong quá trình tìm lỗi. Lỗi do env() không mới, nhưng nếu bạn kể lại nó một cách có hệ thống như trên, nhà tuyển dụng chắc chắn sẽ gật gù đánh giá cao sự chuyên nghiệp của bạn.

*** Anh em đã từng có kỷ niệm debug nào "nhớ đời" muốn chia sẻ không? Cùng thả bình luận giao lưu nhé! Nhớ Upvote để series của chúng ta có thêm động lực ra mắt các kỳ tiếp theo!


All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí