Không Phải Thuật Toán. Mà Là Mã Hóa.

Trong vài ngày, tôi đã chắc chắn rằng hệ thống của mình có một lỗi sâu xa, bí ẩn. Log của tôi hiện ra như rác. Một bước chuyển đổi dữ liệu tạo ra những tệp sai một cách tinh vi. Một tiến trình con trả về thứ vô nghĩa thay vì kết quả tôi mong đợi. Tôi đi săn lùng một sai sót trong logic của mình, ở đâu đó trong phần thông minh của hệ thống.

Không có sai sót nào trong logic cả. Lỗi nằm ở chỗ máy tính và tôi bất đồng về cách đánh vần.

Lỗi không nằm trong code

Điều khiến nó phát điên là mọi thứ trông đều đúng. Tôi có thể đọc đoạn code đã tạo ra rác và không thấy có gì sai với nó. Những thất bại thì kỳ lạ và không nhất quán: một số văn bản hiện ra ổn, một số hiện ra lộn xộn, một số tệp mở hoàn hảo còn những tệp khác lại hỏng một cách tinh vi. Không có điều gì trong mẫu hình đó chỉ về một dòng duy nhất mà tôi có thể đến và sửa.

Sự không nhất quán đó, sau này tôi mới biết, là một manh mối thường gặp. Nhưng lúc ấy nó chỉ trông như thể chương trình của tôi bị ám.

Điều thực sự sai: hai chương trình, hai bảng chữ cái

Đây là cốt lõi của vấn đề. Máy tính không lưu trữ văn bản dưới dạng các chữ cái. Chúng lưu trữ dưới dạng các con số, và để biến những con số đó trở lại thành ký tự, bạn cần một bảng đã được thống nhất — một bộ mã hóa — cho biết con số nào nghĩa là ký tự nào. Nếu hai phần của một hệ thống dùng những bảng khác nhau, thì cùng những byte đó sẽ mang ý nghĩa khác nhau đối với mỗi phần, và văn bản do phần này viết ra sẽ bị phần kia đọc sai.

Đó chính là toàn bộ lỗi của tôi. Hệ điều hành của tôi mặc định dùng một bộ mã hóa cũ kỹ, lỗi thời, trong khi các công cụ và tệp tôi đang làm việc cùng lại giả định bộ mã hóa hiện đại, gần như phổ quát. Mọi thứ hoạt động chừng nào hai bảng tình cờ thống nhất với nhau — điều mà chúng thực sự làm, đối với những ký tự đơn giản nhất — và đổ vỡ ngay khoảnh khắc bất cứ thứ gì bước ra ngoài vùng chồng lấn đó. Văn bản chưa bao giờ bị hỏng một cách có chủ đích. Nó chỉ đang được đọc bằng một ngôn ngữ khác với ngôn ngữ mà nó đã được viết ra.

Tại sao lại khó nhận ra đến vậy

Một sự bất khớp mã hóa là một loại lỗi đặc biệt khó chịu vì nó thường không thất bại một cách rõ ràng. Thay vì ném ra một ngoại lệ chỉ thẳng vào vấn đề, một phép chuyển đổi dễ dãi có thể tạo ra kết quả gần như đúng, cho đến khi nó gặp một ký tự mà hai bộ mã hóa bất đồng với nhau — rồi nó lặng lẽ làm hỏng đúng phần đó và tiếp tục đi tới.

Vậy nên những thất bại mang tính ngắt quãng và phụ thuộc vào nội dung, đó chính xác là kiểu khiến bạn đi tìm sai chỗ. Những lỗi ngắt quãng, phụ thuộc nội dung khiến bạn cảm thấy rằng chúng hẳn phải nằm trong logic của mình, trong cái phần phức tạp mà bạn tự hào. Lỗi này lại nằm trong phần đường ống, trong một giá trị mặc định mà tôi chưa bao giờ để ý rằng mình đang dựa vào.

Sự thật chẳng hào nhoáng của loại công việc này

Tôi đã tưởng tượng rằng những phần khó khăn của việc xây dựng một hệ thống học máy sẽ là những phần thú vị — các mô hình, toán học, chiến lược. Hóa ra một tỷ lệ thực sự lớn của những phần khó khăn thực tế lại là những thứ như thế này. Mã hóa văn bản. Ký tự xuống dòng. Đường dẫn tệp mang ý nghĩa khác nhau trên những máy khác nhau. Cái hạ tầng buồn tẻ nằm bên dưới mọi thứ, nơi một giá trị mặc định sai duy nhất có thể lặng lẽ nuốt chửng cả một tuần đời bạn trong khi bạn nhìn chằm chằm vào đoạn code thanh lịch ở bên trên, đinh ninh rằng vấn đề chắc phải nằm ở đó.

Nó khiêm nhường hóa con người, theo một cách hữu ích. Toán học hiếm khi là thứ chặn đứng tôi thường xuyên nhất. Cái phần đường ống mới là thứ đó.

Cách sửa: ngừng tin vào giá trị mặc định, ép buộc sự thống nhất

Việc sửa chữa chẳng có gì tinh tế. Tôi ngừng dựa vào bất cứ bộ mã hóa nào mà mỗi phần của hệ thống tình cờ dùng mặc định, và ép buộc một bộ một cách rõ ràng — bộ hiện đại, phổ quát — tại mọi ranh giới: mọi lần đọc tệp, mọi lần ghi tệp, mọi tiến trình con được khởi chạy. Một khi mọi thành phần được buộc phải thống nhất, bằng văn bản, về cùng một bảng, thì rác ngừng xuất hiện.

Bên cạnh cách sửa chính đó, tôi thêm vào một thói quen phòng thủ nhỏ hơn: ngay từ đầu giữ một số ký tự rắc rối nhất định ra khỏi log của mình — không phải để thay thế cho việc làm đúng mã hóa, mà như một tấm lưới đỡ. Log là nơi duy nhất bạn cần nhất khả năng đọc rõ ràng khi mọi thứ khác đang bốc cháy, và tôi không muốn công cụ mà tôi dựa vào để chẩn đoán vấn đề lại cứ tiếp tục trở thành nạn nhân của cùng một loại sự cố.

Bài học: một giá trị mặc định là một quyết định bạn đã không đưa ra

Bài học sâu xa hơn đã sống lâu hơn cả con lỗi. Mọi “giá trị mặc định” trong ngăn xếp của bạn đều là một quyết định — thường được đưa ra bởi ai đó không biết tình huống cụ thể của bạn, thường là từ nhiều năm trước, vì những lý do mà có thể nay không còn áp dụng nữa. Phần lớn thời gian, những quyết định kế thừa đó vẫn ổn, và bạn không bao giờ phải nghĩ đến chúng. Đó là một phần khiến chúng trở nên nguy hiểm. Khi một trong số chúng tình cờ sai một cách thầm lặng đối với trường hợp của bạn, nó gần như vô hình, bởi vì bạn chưa bao giờ chủ đích chọn nó — và cái thứ bạn chưa bao giờ chọn lại là nơi cuối cùng bạn nghĩ tới việc đi tìm.

Tôi đã mất gần trọn một tuần vào một lựa chọn mà tôi không biết mình đã đưa ra. Giờ đây, khi một thứ gì đó thất bại theo một cách chẳng có ý nghĩa logic nào, một trong những câu hỏi đầu tiên của tôi không còn chỉ là “lỗi của mình ở đâu?”. Mà còn là “mình đang giả định điều gì mà mình chưa bao giờ thực sự quyết định?”.

— Không tín hiệu, không lợi nhuận, không phải lời khuyên đầu tư.