0
0
Lập trình
Admin Team
Admin Teamtechmely

Bài Học Từ Lỗi Phần Mềm: Hiểu Hệ Thống Thay Vì Cú Pháp

Đăng vào 8 tháng trước

• 10 phút đọc

Những Bài Học Quý Giá Từ Một Lỗi Phần Mềm

Khi một thông báo lỗi sản xuất xuất hiện lúc 11:47 PM vào một ngày thứ Năm, người dùng không thể đăng nhập. Dịch vụ xác thực trả về mã lỗi 500. Doanh thu đang giảm với tốc độ 3,000 USD mỗi phút.

Tôi đã là một lập trình viên được ba năm. Tôi biết React, Node.js, MongoDB. Tôi đã hoàn thành hàng chục khóa học trên Udemy. GitHub của tôi đầy những dự án từ khóa học trông ấn tượng nhưng chưa bao giờ nhận được lưu lượng truy cập thực sự.

Nhưng tất cả những điều đó không còn quan trọng nữa.

Sự Hoảng Loạn Bắt Đầu

Nhật ký lỗi không có giá trị gì. Cụm từ "Lỗi máy chủ nội bộ" lặp đi lặp lại trên hàng nghìn mục, như một bản ghi bị hỏng không cung cấp bất kỳ thông tin nào về vấn đề thực sự. Dịch vụ xác thực chạy tốt trên máy cục bộ. Tất cả các bài kiểm tra đơn vị đều vượt qua. Kết nối cơ sở dữ liệu trông khỏe mạnh.

Một bí ẩn sản xuất điển hình: mọi thứ hoạt động cho đến khi không còn hoạt động.

Tôi làm theo những gì mà mọi hướng dẫn đã dạy tôi. Tôi thêm nhiều câu lệnh console.log. Tôi khởi động lại dịch vụ. Tôi kiểm tra các yếu tố rõ ràng—biến môi trường, kết nối mạng, mức sử dụng bộ nhớ. Mọi thứ đều trông bình thường, nhưng người dùng không thể đăng nhập.

Đến 2 AM, tôi cảm thấy tuyệt vọng. Kỹ sư cấp cao đang trực ca, Marcus, cuối cùng cũng tham gia vào kênh sự cố.

Câu hỏi đầu tiên của anh ấy không phải về mã: "Có gì thay đổi trong 48 giờ qua?"

Tôi liệt kê các bản triển khai gần đây—một vài thay đổi giao diện, một số cập nhật lược đồ cơ sở dữ liệu, không có gì ảnh hưởng đến xác thực. Anh ấy hỏi một câu tiếp theo làm tôi dừng lại: "Còn về các phụ thuộc? Thay đổi hạ tầng? Cập nhật dịch vụ bên thứ ba?"

Tôi không biết. Mô hình tư duy của tôi dừng lại ở ranh giới ứng dụng.

Bài Học Thực Sự Bắt Đầu

Marcus đã hướng dẫn tôi về điều mà anh ấy gọi là "tư duy hệ thống"—một cách nhìn nhận ứng dụng của chúng tôi không chỉ là mã cô lập, mà là một phần của một hệ sinh thái lớn hơn bao gồm các dịch vụ, cơ sở dữ liệu, mạng và các phụ thuộc bên ngoài.

Chúng tôi bắt đầu vẽ ra tất cả những gì mà dịch vụ xác thực của chúng tôi tác động đến. Không chỉ là các phụ thuộc trực tiếp, mà còn cả những hiệu ứng thứ hai và thứ ba. Dịch vụ này gọi đến cơ sở dữ liệu người dùng, chạy trên AWS RDS. Nó lưu trữ phiên trong Redis. Nó xác thực token bằng một thư viện JWT. Nó gửi email qua SendGrid. Mỗi kết nối đều là một điểm thất bại tiềm năng.

Sau đó, Marcus đã chỉ cho tôi điều gì đó thay đổi cách tôi tiếp cận việc gỡ lỗi mãi mãi: theo dõi phân tán. Thay vì nhìn vào dịch vụ của chúng tôi một cách cô lập, chúng tôi đã theo dõi một yêu cầu thất bại qua toàn bộ hệ thống.

Và đó là lúc chúng tôi tìm thấy vấn đề.

Phụ Thuộc Ẩn Giấu

Việc xác thực JWT của chúng tôi đã thất bại, nhưng không phải vì mã của chúng tôi. Thư viện mà chúng tôi sử dụng đã thực hiện các yêu cầu HTTP để xác minh các khóa ký từ một dịch vụ bên ngoài—điều mà tôi chưa bao giờ chú ý vì nó diễn ra âm thầm ở phía sau. Dịch vụ đó đã cập nhật API của họ hai ngày trước, thay đổi định dạng phản hồi một chút.

Sự thay đổi này đã tương thích ngược cho hầu hết các trường hợp sử dụng, nhưng phiên bản thư viện của chúng tôi đã được sáu tháng và không thể xử lý cấu trúc phản hồi mới. Khi việc xác thực khóa thất bại, thư viện đã ném ra một lỗi chung chung mà bị nuốt chửng bởi xử lý lỗi của chúng tôi, dẫn đến những thông báo "Lỗi máy chủ nội bộ" vô dụng mà tôi đã nhìn chằm chằm vào trong nhiều giờ.

Cách khắc phục rất đơn giản: cập nhật phiên bản thư viện. Bài học là sâu sắc: hiểu biết về đồ thị phụ thuộc của bạn có giá trị hơn việc thành thạo bất kỳ framework nào.

Tôi đã dành hàng tháng trời để học về các hook của React và các pipeline tổng hợp của MongoDB, nhưng tôi chưa bao giờ nghĩ đến việc vẽ ra những dịch vụ bên ngoài mà ứng dụng của tôi phụ thuộc vào. Tôi đã coi các thư viện bên thứ ba như những hộp đen, tin tưởng rằng chúng sẽ hoạt động mà không hiểu cách chúng hoạt động.

Đêm đó đã dạy tôi rằng các hệ thống sản xuất thường thất bại ở ranh giới—những nơi mà mã của bạn gặp gỡ thế giới bên ngoài.

Các Mẫu Sâu Hơn

Trong những tuần tiếp theo, Marcus đã chỉ cho tôi cách tư duy này áp dụng ở mọi nơi. Mỗi sự cố mà chúng tôi đã gặp trong năm qua đều theo cùng một mẫu: có điều gì đó thay đổi trong một hệ thống lân cận, và sự cố lan truyền qua các phụ thuộc ẩn.

Sự chậm trễ của cơ sở dữ liệu đã gây ra thời gian chờ trong API của chúng tôi? Một cửa sổ bảo trì định kỳ của AWS đã làm tăng độ trễ lên 50ms—đủ để đẩy các truy vấn được tối ưu kém của chúng tôi vượt qua ngưỡng thời gian chờ.

Các lỗi tải hình ảnh đã làm chúng tôi bối rối trong nhiều ngày? Nhà cung cấp CDN của chúng tôi đã âm thầm thay đổi định dạng phản hồi lỗi của họ, và logic thử lại của chúng tôi đang tìm kiếm các mã lỗi cụ thể không còn tồn tại.

Quy trình thanh toán mà ngẫu nhiên thất bại cho 10% người dùng? Một nhà xử lý thanh toán đã triển khai A/B testing thay đổi cấu trúc payload webhook của họ, phá vỡ hệ thống xác nhận đơn hàng của chúng tôi.

Không có vấn đề nào trong số này có thể được giải quyết bằng cách học một framework JavaScript mới. Chúng yêu cầu hiểu biết về cách các hệ thống tương tác dưới áp lực.

Công Cụ Thực Sự Hữu Ích

Đây là nơi mà các công cụ AI hiện đại có thể tăng tốc việc học—nhưng chỉ nếu bạn sử dụng chúng để xây dựng mô hình tư duy, chứ không chỉ để tạo mã.

Khi tôi gỡ lỗi các tương tác hệ thống phức tạp bây giờ, tôi sử dụng Claude không phải để viết mã xử lý lỗi, mà để giúp tôi suy nghĩ về các chuỗi phụ thuộc. Tôi sẽ mô tả một hệ thống thất bại và yêu cầu nó giúp tôi vẽ ra các điểm thất bại tiềm năng mà tôi có thể đã bỏ lỡ. AI không giải quyết được vấn đề, nhưng nó giúp tôi suy nghĩ một cách hệ thống hơn về những gì có thể sai.

Tôi sử dụng GPT-4o mini để phân tích các mẫu lỗi trên nhiều dịch vụ. Thay vì hỏi "làm thế nào để khắc phục lỗi này," tôi hỏi "các loại thay đổi hệ thống nào thường gây ra mẫu lỗi này?" Nó giúp tôi xây dựng khả năng nhận dạng mẫu áp dụng cho nhiều công nghệ khác nhau.

Trợ lý nghiên cứu trở thành vô giá khi tôi cố gắng hiểu tại sao một dịch vụ bên thứ ba hoạt động theo cách mà nó làm. Thay vì chỉ đọc tài liệu, tôi có thể phân tích các giao thức và tiêu chuẩn cơ bản để dự đoán cách mà các thay đổi có thể ảnh hưởng đến hệ thống của tôi.

Nhưng sự chuyển biến quan trọng nhất không nằm ở các công cụ—mà là ở những câu hỏi bạn đặt ra.

Những Câu Hỏi Quan Trọng

Các lập trình viên junior thường hỏi: "Làm thế nào để tôi làm cho điều này hoạt động?"

Các lập trình viên senior lại hỏi: "Điều này sẽ gây ra vấn đề gì?"

Sự khác biệt không phải là sự bi quan—mà là tư duy hệ thống. Khi bạn hiểu cách mọi thứ hỏng, bạn có thể thiết kế chúng để không bị hỏng. Khi bạn vẽ ra rõ ràng các phụ thuộc, bạn có thể theo dõi các chỉ số đúng. Khi bạn theo dõi dòng dữ liệu từ đầu đến cuối, bạn có thể dự đoán nơi sẽ xuất hiện các nút thắt.

Lỗi xác thực đó đã dạy tôi cách đặt ra những câu hỏi khác:

  • Những dịch vụ bên ngoài nào mà mã này phụ thuộc vào?
  • Làm cách nào để tôi biết khi chúng khỏe mạnh?
  • Điều gì xảy ra nếu chúng phản hồi chậm?
  • Điều gì sẽ xảy ra nếu chúng thay đổi API của họ?
  • Làm cách nào để tôi xử lý những thất bại đó một cách linh hoạt?

Các câu hỏi này không có câu trả lời từ hướng dẫn. Chúng yêu cầu hiểu biết về hệ thống cụ thể của bạn, người dùng cụ thể của bạn, và các ràng buộc kinh doanh cụ thể của bạn. Chúng yêu cầu loại tư duy chỉ có được từ việc vật lộn với các hệ thống sản xuất mà người thực sự phụ thuộc vào.

Sự Thật Khó Chịu

Hầu hết các lập trình viên học bằng cách xây dựng—tạo ra các ứng dụng todo, các dự án clone, các ứng dụng hướng dẫn. Điều này có hiệu quả cho cú pháp và các mẫu cơ bản, nhưng không dạy bạn cách các hệ thống thất bại.

Các ứng dụng hướng dẫn không có người dùng làm mới trang điên cuồng khi xác thực bị hỏng. Chúng không có các nhà xử lý thanh toán âm thầm thay đổi API. Chúng không có các cơ sở dữ liệu chậm lại dưới tải hoặc mạng bị mất gói hoặc các dịch vụ bên thứ ba ngừng hoạt động trong suốt thời gian ra mắt sản phẩm của bạn.

Khoảng cách giữa kiến thức từ hướng dẫn và kiến thức thực tiễn không được lấp đầy bằng nhiều hướng dẫn hơn. Nó được lấp đầy bằng kinh nghiệm với sự thất bại.

Xây Dựng Trực Giác Về Sự Thất Bại

Bạn không thể mô phỏng trải nghiệm này, nhưng bạn có thể tăng tốc nó. Bắt đầu suy nghĩ như một người bi quan về các hệ thống bạn xây dựng. Vẽ ra các phụ thuộc của bạn một cách rõ ràng. Theo dõi các dịch vụ bên thứ ba mà bạn dựa vào. Xây dựng các bảng điều khiển không chỉ cho bạn biết liệu mã của bạn có hoạt động hay không, mà còn cho bạn biết liệu toàn bộ hệ thống có khỏe mạnh hay không.

Sử dụng các công cụ như tính năng phân tích hệ thống của Crompt để giúp bạn theo dõi các tương tác phức tạp, nhưng hãy nhớ: công cụ không thay thế cho tư duy. Nó khuếch đại nó.

Khi bạn xây dựng một thứ gì đó mới, hãy dành thời gian để hiểu những gì bạn đang xây dựng trên đó. Đọc mã nguồn của các thư viện mà bạn phụ thuộc vào. Hiểu các giao thức mà các dịch vụ của bạn sử dụng để giao tiếp. Biết điều gì xảy ra khi các API bên ngoài chậm chạp, hoặc ngừng hoạt động, hoặc trả về dữ liệu không mong muốn.

Điều này không phải là sự hoang tưởng—mà là sự chuyên nghiệp.

Trò Chơi Dài Hạn

Lỗi xác thực đó đã khiến công ty chúng tôi mất 40,000 USD doanh thu và lòng tin của khách hàng. Nhưng nó dạy tôi một điều còn giá trị hơn nhiều: cách suy nghĩ theo hệ thống, không chỉ mã.

Mọi lỗi phức tạp kể từ đó đã tuân theo cùng một cách tiếp cận gỡ lỗi mà Marcus đã chỉ cho tôi đêm đó. Vẽ ra hệ thống. Theo dõi dòng chảy. Tìm ranh giới nơi mọi thứ hỏng. Hiểu phụ thuộc nào đã thất bại.

Các framework mà tôi biết ba năm trước giờ đây hầu như không còn liên quan. React Router trở thành Next.js rồi thành cái gì đang thịnh hành tháng này. Nhưng tư duy hệ thống? Đó là điều có thể chuyển giao cho mọi ngôn ngữ, mọi nền tảng, mọi kiến trúc mà tôi sẽ từng làm việc.

Kỹ năng quý giá nhất mà bạn có thể phát triển với tư cách là một lập trình viên không phải là việc thành thạo framework mới nhất. Mà là học cách nhìn thấy các kết nối ẩn giúp các hệ thống phần mềm hoạt động—và hiểu cách mà những kết nối đó hỏng khi chịu áp lực.

Lỗi sản xuất tiếp theo của bạn đang chờ dạy bạn điều gì đó không hướng dẫn nào có thể dạy được. Câu hỏi là: bạn có sẵn sàng học hỏi từ nó không?

Gợi ý câu hỏi phỏng vấn
Không có dữ liệu

Không có dữ liệu

Bài viết được đề xuất
Bài viết cùng tác giả

Bình luận

Chưa có bình luận nào

Chưa có bình luận nào