Thứ vô tình trở thành kết cấu chịu tải
(jefftk.com)- Ngay cả những cấu trúc hoặc đoạn mã vốn trông nhỏ nhặt hay tạm thời ban đầu cũng có thể dần đảm nhận vai trò nâng đỡ hệ thống theo thời gian, nên trước khi thay đổi cần kiểm tra cả các quan hệ phụ thuộc hiện tại
- Chesterton's Fence là một nguyên tắc hữu ích: trước khi loại bỏ thứ gì, hãy hiểu vì sao nó được dựng lên; nhưng nếu chỉ nhìn vào ý định ban đầu, ta có thể bỏ sót những vai trò phát sinh về sau
- Trong lúc sửa phòng tắm ở nhà, một thanh đứng tưởng như vật cản thực ra vẫn đang gánh một phần tải trọng tầng 2 sau khi mục đích làm vách ngăn tủ quần áo không còn nữa, do những thay đổi kết cấu sai trước đó
- Trong các hệ thống máy tính phức tạp, lịch sử thay đổi và tài liệu thiết kế chỉ là điểm khởi đầu; cần xem thêm cách một thành phần hiện đang được tích hợp vào hệ thống ra sao
- Khi loại bỏ hoặc sửa một thành phần cũ, nếu không đồng thời kiểm tra lý do thiết kế trong quá khứ và vai trò ẩn hiện tại của nó, bạn có thể tạo ra sự cố ngoài dự kiến
Điều mà Chesterton's Fence có thể bỏ sót
- Chesterton's Fence là ý tưởng cho rằng trước khi thay đổi hoặc loại bỏ một thứ gì đó, trước hết phải hiểu vì sao nó được tạo ra
- Những thứ do con người tạo nên như hàng rào hay cánh cổng thường có khả năng cao là từng được xem là có ích cho ai đó vì một lý do nào đó
- Ngay cả một thiết kế trông hoàn toàn vô nghĩa cũng có thể là trường hợp người định thay đổi đã bỏ sót một khía cạnh của vấn đề
- Tuy vậy, góc nhìn này có thể khiến ta chỉ tập trung vào vai trò mà người tạo ra ban đầu đã định, và vì thế bỏ qua những phụ thuộc mới xuất hiện sau đó
Khả năng chịu tải vô tình được phát hiện trong lúc sửa nhà
- Vài năm trước, khi làm lại phòng tắm trong nhà, có một thanh đứng cản trở công việc
- Thanh đó ban đầu là một phần của vách ngăn tủ quần áo, và vai trò vách ngăn đó có vẻ không còn cần thiết nữa
- Nếu chỉ theo góc nhìn của Chesterton's Fence thì có vẻ có thể tháo bỏ, nhưng trên thực tế theo thời gian nó đã trở thành một kết cấu chịu tải
- Sau những thay đổi kết cấu sai khác, thanh này đã góp phần đỡ tầng 2 của ngôi nhà
- Không chỉ cần xem một thứ ban đầu được tạo ra vì sao, mà còn phải kiểm tra xem sau đó nó đã đảm nhận vai trò bổ sung nào
Bài học áp dụng cho các hệ thống máy tính phức tạp
- Khi thay đổi các hệ thống máy tính phức tạp, vấn đề tương tự cũng lặp lại
- Việc xem lại lịch sử thay đổi, đọc tài liệu thiết kế ban đầu và hiểu vì sao một thành phần được làm theo cách đó vẫn rất hữu ích
- Nhưng để an toàn, còn phải xem thành phần đó hiện đang được kết nối và sử dụng trong hệ thống theo cách nào
- Các thành phần rất dễ dần đảm nhận những vai trò phụ khác với mục đích ban đầu theo thời gian
- Một thay đổi an toàn bắt đầu từ việc đồng thời xác nhận ý đồ thiết kế trong quá khứ và vai trò thực tế ở hiện tại
1 bình luận
Các ý kiến trên Hacker News
Cuối cùng cũng có cảm giác là đã có tên để gọi chuyện này
Tôi làm khá nhiều việc hỗ trợ hệ thống điều khiển, và không hiếm khi mã PLC xử lý một thiết bị vật lý nào đó theo cách đặc biệt lại tạo ra vấn đề ngoài ý muốn
Tôi thường nhắc đi nhắc lại câu: “Mỗi lần bạn sửa một vấn đề điện/cơ khí bằng phần mềm, một con gremlin lại ra đời”
Dù tìm ra nguyên nhân gốc của một lỗi, hoặc một giới hạn đã được lập trình mà mình muốn loại bỏ, tôi luôn từ chối cho đến khi biết vì sao đoạn mã đó tồn tại. Không có đoạn mã nào được đưa vào mà không có lý do, nên trước hết phải hiểu vì sao cần bộ hẹn giờ hay ghi đè đó
Nếu đó là mã từng giải quyết một vấn đề nay đã biến mất thì thường là tin tốt, nhưng cũng có nhiều trường hợp mã được đưa vào để ngăn một sự cố nào đó, rồi do thay đổi nhân sự mà mục đích ban đầu bị thất truyền. Khi không có tài liệu, tôi trở nên rất thận trọng với việc lập tức đảo ngược công việc của người khác
Cũng có yếu tố tin tưởng đồng nghiệp. Thường thì người ta không làm điều gì đó vô cớ, nên nếu có mã ở đó thì nên xem là nó có mục đích, và tin rằng ban đầu nó đã được xem xét đủ kỹ. Khi niềm tin đó sụp đổ, mọi quyết định đều trở nên khó khăn
Nếu lý do là tương tác với thứ gì đó bên ngoài codebase như hệ điều hành, hệ thống tệp, cơ sở dữ liệu, endpoint HTTP, phần cứng, thì miễn không phải chỉ là một lời gọi API hay thư viện đơn giản, tôi sẽ chú thích 100%
Nếu thêm
sleepvì giới hạn tốc độ của một dịch vụ khác, tôi ghi rõ ai yêu cầu, giá trị giới hạn trong phạm vi biết được lúc đó, nếu không biết chính xác thì ghi “chỉ là ước đoán nhưng có vẻ hoạt động”, và hệ thống có thể trông như thế nào khi vượt quá giới hạnNếu dùng cơ sở dữ liệu cho một việc nhỏ nhặt có vẻ làm bằng hệ thống tệp cũng được, nhưng thực tế trong môi trường đó, cách truy cập hệ thống tệp cần thiết sẽ làm cạn kiệt tài nguyên do các system call bùng nổ khi chịu tải, tôi sẽ để lại chú thích
Các workaround cho lỗi thư viện phổ biến mà Ubuntu không sửa trong bản phát hành LTS cũng là đối tượng cần chú thích. Tôi đã viết rất nhiều chú thích kiểu “tôi biết cái này không hay, nhưng lý do là như sau”
Khi viết đoạn mã mà hiện tại lười làm cho có khả năng mở rộng và theo dự đoán hiện nay cũng không cần, nhưng sẽ không chạy tốt ở quy mô lớn, tôi cũng để lại chú thích. Có lẽ phần nào là để bảo vệ tự ái, nhưng kiểu như: “Tôi biết là đang đọc toàn bộ tệp vào bộ nhớ, nhưng đây là batch job hiếm khi chạy, có thể dự đoán được, và tệp dự kiến cũng nhỏ nên ổn. Nếu thiếu bộ nhớ thì hãy xem chỗ này trước. Nếu định đổi thành gọi theo nhu cầu thì hãy viết lại.”
Điều bài viết muốn chỉ ra là chỉ như vậy thì chưa đủ. Vì còn phải biết những thứ gì khác đã được xây dựng trên giả định rằng đoạn mã đó tồn tại
“Không biết vì sao cần rung này, nhưng cứ xóa nó rồi tự kiểm chứng đi”
Tôi không động vào, và cũng không thử kiểm chứng
Các kỹ sư điện và kỹ sư cơ khí trong lịch sử thường xem phần mềm kém nghiêm túc hơn hệ thống điện/cơ khí, và kết quả là trong văn hóa kỹ thuật do EE/ME chi phối, rất dễ sinh ra mã lộn xộn
Ngay cả trong số những người bề ngoài là Professional Engineer, trình độ kỹ nghệ phần mềm non nớt đến mức khó chấp nhận vẫn còn phổ biến
Chưa nói đến mã không có tài liệu, phần cứng thường là đồ tùy biến từ 30 năm trước, đến mức thậm chí không có sơ đồ mạch của thiết bị đang làm việc
Tôi từng gặp chuyện tương tự
Vài năm trước tôi mua một căn nhà cũ, và các chủ trước đã tự làm hầu hết mọi việc từ thập niên 1960
Máng xối kẽm có lẽ đã rò rỉ suốt hàng chục năm, làm hỏng một phần kết cấu mái, và mái nhà đang được chống bởi các tấm ốp gỗ được gắn vào hồi thập niên 70 để che phần bên trong. Nói cách khác, các tấm ốp gỗ đó thực sự đang chịu tải
Tôi còn phát hiện nhiều thứ hơn nữa trong căn nhà này. Ví dụ, ở phần rìa mái, khi chiều rộng ngói không đủ, thay vì mua thêm ngói, họ đã trám bằng xi măng và mảnh vỡ của chậu gốm
Ngày nay, người ta trông chờ vào tấm thạch cao và ván ép để đảm nhận vai trò đó trong động đất và bão
Khi thấy một căn nhà bị tháo chỉ còn khung rồi xây lại, bạn sẽ thấy có thêm vài thanh giằng. Không phải để ngăn tường sập, mà để giữ góc vuông và mặt phẳng cho đến khi dựng lại tường
Thoạt nhìn tưởng như ai đó đã đâm vào cửa gara làm nó móp nặng, nhưng nhìn kỹ thì thấy mái nhà đang bám víu vào đường ray gắn cửa, gần như sắp tiêu rồi
Ban đầu tôi chỉ định nối các đầu xà mái lại và thay cửa gara, kiểu bên kia trước đây cũng có ai đó làm vậy rồi nên nếu chạy được là được, nhưng giờ có lẽ phải làm lại toàn bộ mái
Điều thực sự đáng lo là hệ thống dây điện đáng ngờ chạy khắp tầng hầm. Dây khá mới, dây cũ bọc vải, và băng keo điện nối chúng lại với nhau lẫn lộn. May là có vẻ không dây nào trong số đó đang chịu tải
Bài viết này và gần như tất cả bình luận dường như đều bỏ lỡ vấn đề thật sự. Cốt lõi là thiếu kiểm thử
Khác với mọi phương tiện sản xuất khác, phần mềm có thể thực sự kiểm thử các thay đổi trước khi phản ánh chúng vào thực tế
Nếu có kiểm thử tốt, việc ý định ban đầu là gì, tính năng đó có mục đích sử dụng mới hay người dùng mới hay không đều không quan trọng. Sửa rồi chạy kiểm thử, nó sẽ cho biết bản sửa đó có tốt hay không
Nếu có kiểm thử tốt, bạn không cần khảo cổ phần mềm, những cựu binh lão luyện biết mọi vết nứt, thần đồng mô hình hóa hệ thống phức tạp trong đầu, tài liệu yêu cầu bao quát, hay hệ thống triển khai thận trọng biến một số nhóm người dùng thành chuột bạch
Nếu có kiểm thử tốt, bạn thậm chí có thể thay đổi hệ thống ngẫu nhiên rồi dừng lại khi có cải thiện. Chính xác giống cách Google báo cáo rằng AI đã “phát triển” một cải tiến về căn chỉnh
Thế nhưng lập trình viên kiểm thử vẫn được trả lương chưa bằng một nửa, bộ phận kiểm thử tương đối nhỏ, QA bị ép vào lịch trình cố định và hạn chế, còn các anh hùng kỹ thuật xuất thân từ QA thì hầu như không có. Có lẽ vì nó trông như công việc phái sinh và phản ứng lại
Bạn có thể đã xóa mã không dùng và kiểm thử của nó, nhưng thực ra nó vẫn đang được dùng
Sau khi thay đổi, kiểm thử thất bại; bạn sửa kiểm thử cho phù hợp tình huống mới vì nghĩ kiểm thử mong manh, nhưng hóa ra có thứ nào đó đang phụ thuộc vào hành vi cũ
Kiểm thử rất tuyệt, và với các hệ thống đủ tự khép kín thì chỉ vậy có thể đã đủ. Nhưng trong các hệ thống lớn hơn, đôi khi cũng cần telemetry hoặc triển khai theo từng giai đoạn
Mã ban đầu được dùng để tính VAT cho danh sách mua hàng, nhưng dần dần có thể trở thành phương tiện buộc làm mới cache VAT theo danh mục sản phẩm và được gọi trong những ngữ cảnh không lường trước ban đầu
Chú thích cũng vậy. Chúng nói về ý định ban đầu và tác dụng phụ, nhưng không thể nói về việc rất lâu sau phương thức hay lớp đó được dùng ở đâu, và trên thực tế đã trở thành thứ làm gì
Trong một thế giới lý tưởng, khi thế giới xung quanh thay đổi thì chú thích cũng được cập nhật, nhưng trên thực tế hầu như không ai làm vậy trừ khi mã nội bộ cũng thay đổi theo
Thường thì kiểm thử sẽ mục nát dần. Có vẻ kiểm thử cũng có hạn sử dụng, và cuối cùng một số kiểm thử bắt đầu chết
Các vấn đề phụ thuộc, thay đổi kỳ vọng API, cập nhật bảo mật, tài khoản và thông tin xác thực hết hạn, endpoint máy và thay đổi trạng thái trộn lẫn khiến kết quả kiểm thử không còn chỉ ra được chương trình có đúng hay không
Giá trị kinh doanh biên của việc sửa một kiểm thử hỏng riêng lẻ thường rất thấp, nên người ta hay tắt hẳn nó đi hoặc ép nó “đậu” dù lẽ ra phải báo lỗi
Lặp lại 10, 20 năm, chẳng mấy chốc sẽ tách thành “những kiểm thử thực sự được tin cậy” và “những kiểm thử quá bận nên không sửa hoặc dọn được”
Việc kiểm thử nào tốt hay xấu trở thành tri thức bộ lạc biến mất theo thay đổi công việc và vai trò, rồi đến một lúc nào đó chính khối “kiểm thử nói dối rằng mọi thứ hoạt động” và “kiểm thử mà người ta không còn xét xem thất bại có thật hay không” lại tình cờ gánh tải
Thà nói rằng lập trình viên nên viết kiểm thử, lưu chúng cùng với mã và tự động chạy trong quá trình build thì tốt hơn
Nhưng tôi nghĩ ngay cả phát triển hướng kiểm thử đúng nghĩa cũng không thể thay thế thiết kế tốt và thực hành tốt. Ngay cả một đặc tả rất đơn giản cũng không thể được thay thế bằng kiểm thử
Nếu
f(S)chỉ được đặc tả là trả về chuỗi bằng cách nối chuỗi đó với chính nó, thì chỉ với các kiểm thử hiển nhiên xemfnhư hộp đen, rất khó xác minh rằngflà đúng. Đặc tả hình thức cũng quan trọngBạn có thể thử vài điểm, nhưng nếu có tình huống mà một giá trị sai như ma thuật có thể gây hậu quả nghiêm trọng, kiểm thử sẽ không cho thấy được điều đó
Có thể châm biếm khảo cổ phần mềm, cựu binh lão luyện, thần đồng mô hình hóa hệ thống trong đầu, tài liệu yêu cầu bao quát, và hệ thống triển khai lấy một số người dùng làm chuột bạch, nhưng tất cả đều là cách ứng phó nảy sinh vì phần mềm khó. Và phần mềm thật sự rất khó
Thông thường, đội phần mềm phải chịu trách nhiệm trực tiếp về chất lượng công việc của mình, và không thể đẩy vấn đề sang phía bên kia sơ đồ tổ chức
Có thể hiểu việc một cây đinh tán vốn không quan trọng về sau lại phải chịu tải, nhưng theo kinh nghiệm của tôi, đó trông giống dấu hiệu của thiết kế lười biếng
Ít nhất khi làm phần mềm, bạn có thể biết rằng mình đang cố dùng một cây đinh tán trang trí để chống đỡ một phần ngôi nhà; nếu bạn chọn cứ để vậy thay vì tạo một cấu trúc mới tốt hơn, về sau đội phát triển sẽ khá buồn
Tôi đồng ý với bài viết, nhưng làm ở một nơi mà bạn có thể kỳ vọng sẽ không thường xuyên phát hiện ra những chuyện như thế vẫn tốt hơn nhiều
Ý chính của bài này không hẳn là đừng dùng đinh tán trang trí làm phần tử chịu tải, mà gần hơn với việc hãy nhận ra rằng trước khi bạn đến, có thể ai đó đã làm như vậy
Đây còn là lập trường bảo thủ hơn cả cách diễn giải cơ bản về hàng rào Chesterton, mà ngay cả cách diễn giải cơ bản đó cũng bị nhiều người gạt đi vì cho là quá hạn chế
Với tôi, bài này rất có sức cộng hưởng. Nói theo kiểu lập trình, tôi đã thật sự từng gỡ một đường chỉ “trang trí” rồi trần nhà sập xuống đầu
Hệ thống có vững chắc hay nguy hiểm vì không thể mở rộng còn tùy vào bối cảnh
Ta luôn có thể làm các thí nghiệm tư duy kiểu “nếu đội sales tăng gấp đôi và bán cũng như onboarding khách hàng nhanh nhất có thể cho đến khi chiếm 100% thị trường thì sao?”, và ngay cả trong điều kiện đó, việc dùng cơ sở dữ liệu như message queue vẫn có thể ổn
Nếu kết quả là đội phát triển khổ sở thì đó là trường hợp mắc lỗi. Nó trở nên khó bảo trì hoặc thành địa ngục vận hành
Nhưng dùng đinh tán trang trí trong phần mềm làm phần tử chịu tải không nhất thiết sẽ dẫn đến kết quả như vậy. Có rất nhiều hệ thống tiết kiệm được vài tháng xây lời giải đúng đắn, mà vẫn âm thầm và vui vẻ làm tròn vai của mình
Phần còn lại của codebase tuyệt đối không bao giờ gửi đầu vào sai, nên nhánh đó là code chết và không chịu tải
Rồi đến một lúc nào đó, một lỗi xuất hiện và gửi đầu vào sai, nhánh đó cần mẫn xử lý và khôi phục. Ngay khoảnh khắc ấy, nhánh đó trở thành nhánh chịu tải
Sản phẩm “vô tình chịu tải” tôi thích nhất trong những gì mình từng thấy là một cấu hình
sudosaiNó cho phép
sudokhông cần mật khẩu với lệnhfind, nên rất dễ thực thi mã tùy ý với quyền root qua-exec, và nhiều script hỗ trợ quan trọng của sản phẩm đã được viết để dùng chính điều đóNói cách khác là leo thang đặc quyền chịu tải
Vài năm trước tôi đã sửa sang lại bếp
Ở một đầu bếp cũ có một dầm lớn chạy ngang, được thêm vào trong một lần cải tạo nào đó trước khi chúng tôi mua nhà để nâng tầng hai lên. Muốn mở rộng bếp thì phải bỏ nó đi
Khi tháo trần ra, chúng tôi thấy dầm đó nằm lệch khoảng 2 foot về bên phải so với vị trí nó phải đỡ bức tường tầng trên
Cuối cùng chúng tôi đã sửa và chuyển dầm vào trong bức tường tầng trên, mọi thứ ổn thỏa; nhưng khi hỏi về vị trí ban đầu, nhà thầu đại khái nói thế này
“Đã có một người muốn làm cho đúng và một người không quan tâm. Chất lượng rốt cuộc sẽ bị kéo về mức thiết lập thấp nhất thôi”
Một điểm phần mềm hơn hệ thống vật lý là bạn có thể dễ dàng ghi tài liệu ngay trong mã bằng chú thích và kiểu để làm rõ ý định hơn
Đặc biệt trong các ngôn ngữ động như Python thì không hoàn hảo, nhưng giúp ích rất nhiều
Phép ẩn dụ tương ứng với đinh tán chịu tải có thể là một dự án hackathon mà bạn không nghĩ sẽ lên production
Trên thực tế, rất nhiều việc chúng ta làm là hack một thứ gì đó cho đến khi nó vừa đủ chạy, rồi chuyển sang việc tiếp theo
Bởi kế hoạch bảo trì cần biết từng linh kiện hay cụm lắp ráp có thể thay thế đang chịu “tải” nào
Đáng tiếc là kỹ thuật hệ thống ngày nay đã đi rất xa khỏi mục tiêu ban đầu, nhưng ý tưởng ban đầu là như vậy
Một lý do khiến các bộ phận kỹ thuật hệ thống ngày nay tương đối yếu thế hơn là tài chính đã chen vào kế hoạch bảo trì. Khấu hao hàng tồn kho rất tàn nhẫn, và “nên giữ gì làm phụ tùng dự phòng” ít nhất theo kinh nghiệm của tôi giờ hiếm khi còn là phán đoán của kỹ thuật hệ thống
Kết quả thì có thể dự đoán, dù phần nào được bù lại bởi tiêu chuẩn rất cao của nhân lực bảo trì hàng không vũ trụ. Chẳng hạn so với thợ sửa máy giặt, họ xuất sắc hơn đáng kể
Tất nhiên bộ phận tài chính cũng muốn hạ tiêu chuẩn đó xuống vài nấc
Nhưng tôi luôn ngạc nhiên trước việc thường xuyên thấy các hệ thống trong đó một thành phần thượng nguồn trông có vẻ “trang trí” thực ra đang áp giới hạn tốc độ, và nếu gỡ bỏ thì phần còn lại sẽ chạy mất kiểm soát
Tôi nghĩ đến những trường hợp người dùng vô thức tận dụng một lỗi trong phần mềm rồi tích hợp nó vào luồng công việc bình thường của họ
Kết quả là khi sửa lỗi, luồng công việc bị đứt gãy và phát sinh phàn nàn
Bài nói “rất dễ biết vì sao nó ở đó. Nó là một phần của vách ngăn tủ âm tường”, nhưng theo thời gian nó đã vô tình trở thành phần chịu tải, và thông qua các thay đổi kết cấu sai khác, cây đinh tán đó giờ đang giúp chống đỡ tầng hai của ngôi nhà
Nhưng rõ ràng là không hề dễ biết vì sao nó ở đó. Hơn nữa, tôi cũng không bị thuyết phục rằng nó đã vô tình trở thành phần chịu tải
Khá có khả năng nó đã được cố ý cho chịu tải vì những lý do mà bạn thấy là sai, nhưng với người thời đó thì không sai
Chỉ là việc biết vì sao ban đầu nó ở đó không cho bạn biết hiện tại nó đang làm gì
Một nghiên cứu sinh sau tiến sĩ vật lý từng làm việc cùng tôi thỉnh thoảng dán biển như thế này lên các cấu hình thiết bị
“Đừng chạm vào. Có nguy hiểm ẩn”
Phòng thí nghiệm đầy những người thông minh, vốn quen tự mình suy luận hợp lý xem nhìn thấy thứ gì đó thì có được thay đổi hay không
Tấm biển này là một cảnh báo rằng đừng vội đưa ra phán đoán như vậy