Ngón tay giữa của Chesterton
(arp242.net)- Hàng rào của Chesterton là lời khuyên đừng tùy tiện sửa mã khi chưa biết lý do tồn tại của nó, nhưng mã và lịch sử commit không để lại lý do lại đẩy chính gánh nặng đó sang cho những lập trình viên đến sau
- Phần thân commit trong 13 năm qua của kho lưu trữ này chỉ có 295 dòng tính theo lệnh, và nếu bỏ các phần thân liên quan đến dependabot, revert và typo thì giảm còn 167 dòng
- Tiêu đề commit, ngay cả với thay đổi lớn, cũng không cung cấp được ngữ cảnh, kiểu như “fix page A”, và hầu như cũng không có tài liệu riêng hay chú thích trong mã, nên rất khó lần theo lý do của các thay đổi
- Trong mã vẫn còn những đợt refactor dang dở, tàn dư của tính năng đã bị gỡ, các tính năng đã được thêm vào nhưng không được nối hoặc không được dùng, và cả những tính năng có vẻ chẳng ai dùng
- Commit message và tài liệu ít nhất phải để lại “đang thay đổi gì, vì sao thay đổi, và vì sao đây là cách giải quyết tốt”, còn nếu không để lại gì thì chi phí cho những lập trình viên đi sau sẽ tăng lên rất nhiều
Gánh nặng mà mã không có lý do để lại
- Hàng rào của Chesterton là phép ẩn dụ rằng nếu chưa hiểu vì sao một thứ lại được làm như vậy thì đừng vội thay đổi nó
- Trong lập trình cũng có thể xảy ra chuyện bạn “sửa” một đoạn mã trông kỳ quặc rồi sau đó mới nhận ra sự kỳ quặc ấy có lý do của nó
- Trường hợp này lại cho thấy vấn đề ở phía ngược lại
- Có rất nhiều đoạn mã và quyết định kỳ lạ, nhưng không có ghi chép nào cho biết vì sao chúng lại thành ra như vậy
- Sau khi các lập trình viên trước đó đều đã rời đi, cũng không còn ai để hỏi
- Lịch sử commit của kho gần như không cung cấp được ngữ cảnh thực sự nào
- Phần thân commit trong 13 năm qua tổng cộng chỉ có 295 dòng
- Nếu tự tay loại bỏ các phần thân commit của dependabot, “revert commit”, và “fix typo” thì chỉ còn 167 dòng
- Xấp xỉ mức một dòng mỗi tháng
- Tiêu đề commit phần lớn cũng không đủ để giải thích các thay đổi lớn, kiểu như “fix page A”
- Không có tài liệu riêng, và chú thích trong mã cũng gần như không có
- Tình huống này gần với ngón tay giữa của Chesterton hơn là hàng rào của Chesterton: “chúng tôi đã làm rất nhiều thứ kỳ quặc, nhưng sẽ không nói vì sao lại làm vậy”
Những ghi chép tối thiểu mà lập trình viên nên để lại
- Trong codebase còn sót lại nhiều loại mã dang dở hoặc tàn dư khác nhau
- Refactor chưa hoàn tất
- Tàn dư của các tính năng đã bị loại bỏ
- Các tính năng đã được thêm vào nhưng không được nối hoặc không được sử dụng
- Các tính năng có vẻ như chẳng ai dùng
- Nhìn tổng thể, vấn đề Khoảng trống của Chesterton cũng có vẻ khá nghiêm trọng
- Gần giống tình huống kiểu “chưa có hàng rào thì hãy dựng một cái”, thay vì hỏi xem có thật sự cần hàng rào hay không
- Viết hay thì khó, nhưng để lại một lời giải thích ở mức đủ dùng thì không khó
- Về cơ bản, ghi chép thay đổi phải trả lời được ba câu hỏi
- Đang thay đổi gì
- Vì sao thay đổi
- Vì sao cách giải quyết này là tốt
- Có những lúc chỉ cần “Implement new feature X” là đủ, nhưng trong đa số trường hợp vẫn có điều để viết về lý do hoặc cách thức mà tính năng được thêm vào như vậy
- Với sửa lỗi, refactor, và các thay đổi thực chất, thông thường có thể để lại ít nhất một hai đoạn ngắn về nội dung thay đổi và lý do của nó
- Đây không phải việc tùy chọn mà là một phần công việc của lập trình viên phần mềm
- Không cần văn vẻ
- Không cần tiếng Anh hoàn hảo
- Không cần một bài luận hoành tráng
- Dù có thiếu sót, vẫn tốt hơn rất nhiều so với việc không để lại gì
- Nếu không để lại gì cả, bạn đang đẩy vấn đề đó sang cho tất cả những người đến sau
1 bình luận
Ý kiến trên Lobste.rs
Có những lúc không rõ điều gì về sau sẽ trở nên quan trọng. Sẽ rất hữu ích nếu toàn bộ quá trình dẫn tới một commit đều được ghi lại công khai, nhưng vì tất cả những người liên quan đều đã có rất nhiều ngữ cảnh sẵn trong đầu nên cũng sẽ có những phần bị lược bỏ vì bị xem là “quá hiển nhiên”
Nếu thảo luận không được ghi lại, việc lần theo quá trình ra quyết định sẽ khó hơn rất nhiều, như làm khảo cổ học số vậy. Rốt cuộc sẽ có lúc phải xử lý một hàng rào mà không biết vì sao nó tồn tại ở đó, và phải đánh giá nó dựa trên ngữ cảnh hiện tại cùng kiến thức hệ thống của những người đang có mặt
Sẽ rất có ích nếu có người gắn bó lâu dài với dự án để tích lũy hiểu biết tổng thể và trực giác về toàn bộ hệ thống. Những tổ chức xem lập trình viên như bánh răng có thể thay thế thường không giữ được ai đủ lâu, nên cứ lặp lại cùng một sai lầm và phải phát minh lại bánh xe
Nhờ vậy, người tiếp theo nhìn vào đoạn mã đó sẽ không còn có đúng 0% khả năng hiểu được vì sao nó lại như vậy
Tôi chưa bao giờ hiểu được những lập trình viên chỉ viết trong commit message kiểu như “fix” hay “WIP commit”. Có lẽ họ chưa từng thực sự làm khảo cổ học mã nguồn, hoặc thậm chí chưa từng nghĩ rằng chuyện đó là khả thi
Tôi luôn cố mắc lỗi theo hướng cung cấp quá nhiều thông tin. Như vậy thì bản thân tôi trong tương lai, người kế nhiệm, hay người tội nghiệp bị gọi dậy khi có sự cố, ít nhất cũng có cơ hội lần ra vì sao cuối cùng lại có thứ gì đó bị hỏng
Trong những trường hợp như vậy, rất khó để theo dõi trong đầu điều gì đã thay đổi sau mỗi checkpoint hoặc gắn cho nó một mô tả có ý nghĩa, và commit bị đối xử gần giống nút “save” trong trò chơi điện tử hơn là một cách để chia công việc thành các đơn vị được xác định rõ ràng
Ngược lại, nếu tạo ra một commit lớn là kết quả của một đợt refactor API quy mô lớn, thì có lẽ bất kỳ mô tả nào ngắn hơn một tài liệu thiết kế đều sẽ là không đủ, và nếu không định viết đến mức đó thì ý nghĩa của một message dài cũng trở nên mơ hồ. Tuy vậy, có người lại xem điều này như một thứ huân chương rồi bắt đầu viết trong ghi chú phát hành những câu kiểu “sửa lỗi và cải tiến tính năng”, và đó rõ ràng là một kết luận sai lầm
Nhiều lập trình viên thường quên rằng “người khác” phải đọc và hiểu bối cảnh của một thay đổi có thể chính là bản thân họ trong tương lai. Hẳn ai cũng quen với cảnh gãi đầu nhìn một khối mã, rồi chạy
git blamevà thấy chính tên mình staring backMột commit message tốt đã không biết bao nhiêu lần giúp tôi rút ngắn từ vài giờ đến thậm chí vài ngày lần mò qua issue, email, và log chat để tìm ra vì sao. Kể cả trong những trường hợp lẽ ra tôi phải có thể trả lời ngay lập tức thì cũng vậy
Hãy tử tế với bản thân trong tương lai. Tốt hơn hết là đổ hết những gì mình biết, đang nghĩ, và mọi thứ đã được thảo luận vào git log. Sẽ không bao giờ rõ được 5 năm sau điều gì vẫn còn là hiển nhiên
git blamethì, ít nhất với những việc từng có lý do, tôi không hay gặp lắm. Những thứ ngẫu nhiên trông như hành vi kỳ quặc của phía bên kia thì rất khó giải thích cho hữu ích nếu không nhìn được quy trình của họ, nhưng nếu đó là thứ từng có ý nghĩa với tôi, thì khi đọc lại tôi thường vẫn hiểu lại đượcCách tôi học có vẻ gần với kiểu xếp chồng như các tầng dung nham. Từ sau trung học, thay vì thay đổi lớn, tôi chỉ tiếp tục bồi thêm những gì mình biết và những cách mình có thể sử dụng chúng
Gần đây có người cho rằng nếu thông điệp commit dài quá một câu thì nhìn chung là lãng phí thời gian; tôi rất muốn phản bác mạnh mẽ, nhưng lại lúng túng hơn dự kiến khi chứng minh điều ngược lại
Một câu hỏi là: thông tin nào nên nằm trong thông điệp commit, và thông tin nào nên nằm trong chú thích nội tuyến, ADR, hoặc các tài liệu dạng dài khác
Tôi vẫn cố viết thông điệp commit cho tốt, nhưng ở chỗ làm thì gần như đã vô vọng, còn ngay cả trong các dự án cá nhân nghịch cho vui tôi cũng không giữ được sự nhất quán
Nhưng mã cuối cùng vẫn phải có thể hiểu được ngay cả khi không đọc thông điệp commit. Nếu có phần nào trong mã mới cần lý do biện minh, thì điều đó nên được đưa vào chú thích
Nói cách khác, thông điệp commit giải thích vì sao ta thực hiện thay đổi này ngay bây giờ, còn chú thích giải thích vì sao mã ở thời điểm thay đổi đã hoàn tất lại ở trong trạng thái như vậy
Với các thay đổi lớn hơn, đặc biệt là tính năng mới, cần có tài liệu thiết kế ở đâu đó. Nếu cần review thì đó có thể là tài liệu thực sự trong repository, hoặc cũng có thể nằm trong issue tracker
Thông điệp commit nên trỏ tới tài liệu đó, và cũng có thể cần giải thích các chi tiết phát sinh khi thiết kế được chuyển thành mã. Nếu có thể, nên kèm cả một bản tóm tắt ngắn của tài liệu thiết kế. Điều này đặc biệt quan trọng khi có nhiều ứng viên reviewer, vì nó phát tín hiệu ai là người nên quan tâm nhất, và giúp phát hiện trường hợp thiếu mất người lẽ ra phải phản hồi từ giai đoạn thiết kế
git loghayjj log, mà gần như luôn xem qua hiển thị chú thích theo dòngDòng tiêu đề nhìn chung rất hữu ích để quyết định có cần đào sâu thêm hay không. Nếu phần thân có thông tin về lý do thay đổi được thực hiện, thì nó sẽ hữu ích khi lý do đó không trực quan
Ví dụ, ngay cả khi có khá nhiều mã được thêm vào, chỉ cần “admin: add impersonation” là đủ trong nhiều trường hợp. Nhưng với “auth: shorten JWT timeouts” thì tôi muốn thấy một hai câu giải thích vì sao cần rút ngắn thời hạn
Tôi khá cho rằng các thông điệp commit dạng dài thật ra không mấy hữu ích. Lý do nhìn chung cũng giống những gì bài viết đã chỉ ra. Kiểu đó có vẻ xuất phát từ các quy trình làm việc nơi thông điệp commit cũng chính là mô tả PR, chẳng hạn như quy trình dựa trên email hoặc Gerrit. Trong những trường hợp đó thì nó không hẳn có hại, nhưng cũng khó nói là nhất định tạo thêm giá trị
Tôi cũng ở trong tình cảnh tương tự. Trong nhóm rộng hơn ở chỗ làm, chỉ có tôi và một người nữa là viết thông điệp commit chi tiết
Ngay cả khi biết vì sao hàng rào được dựng lên, vẫn có thể không biết vì sao nó vẫn còn ở đó bây giờ. Ngay cả khi là người trực tiếp dựng nên hàng rào Chesterton, bạn cũng chưa chắc biết mình có thể phá nó đi hay không
Cây phụ thuộc lẫn nhau được dự tính vào thời điểm hệ thống được tạo ra chỉ là một tập con của cây phụ thuộc lẫn nhau thực tế ở một thời điểm nào đó. Vì vậy, ngay cả khi một lập trình viên hoàn hảo có thể kể hết lý do vì sao hàng rào được tạo ra, giá trị thực tế của thông tin đó vẫn có giới hạn
Điều họ biết là vì sao nó được tạo ra, chứ không phải câu trả lời cho “nếu bỏ hàng rào này đi thì cái gì sẽ hỏng?”. Cứ xem https://xkcd.com/1172/ là hiểu. Trong các trường hợp sử dụng buồn cười có những cái có thể bị xem là không quan trọng, nhưng luôn tồn tại những cách dùng chính đáng mà ngay cả lập trình viên ban đầu cũng không thể biết trước
Biết người tạo ban đầu đã nghĩ gì hay đã hút gì thì cũng thú vị đấy, nhưng thông tin đó nằm đâu đó giữa không đầy đủ và không liên quan
Lấy một ví dụ bịa ra: giả sử hàng rào Chesterton ban đầu được dựng để giữ trẻ con tránh xa vũng nước khi hai bên còn là nông trại. Giờ đã có đường cao tốc, và hàng rào đó có thể tình cờ trở thành cơ chế duy nhất ngăn các vụ va chạm giữa hươu và ô tô gây chết hàng loạt cho cả động vật lẫn con người
Không ai biết điều đó. Tổ hợp “có đường cao tốc nhưng không có hàng rào” không những chưa từng được thử nghiệm mà còn chưa từng tồn tại, và bên xây đường cao tốc lẫn cơ quan tài nguyên thiên nhiên đều không biết vì sao đoạn đường đó lại có ít vụ cán động vật. Vài năm sau, nếu toàn bộ nông trại biến thành khu dân cư và khu đó không còn là tuyến di chuyển chính của động vật nữa, hàng rào có thể trở nên vô dụng, mà cũng có thể không
Nếu điều này nghe có vẻ gượng ép hoặc có cảm giác không áp dụng cho mình thì tôi ghen tị đấy. Theo kinh nghiệm của tôi, trừ những công ty có quy mô nhất định và chưa quá lâu đời, còn lại phần lớn mọi thứ đều vận hành kiểu này
Sự thật là mọi việc chúng ta làm đều là một phần của hệ sinh thái, phụ thuộc vào và cũng bị phụ thuộc bởi những thứ mà chưa bao giờ có thỏa thuận tương tác một cách rõ ràng. Ta có thể giảm bề mặt API và cố không để toàn bộ chi tiết triển khai trở thành chuyện của hàng xóm, nhưng sự kết dính ngoài ý muốn gần như là một quy luật vũ trụ, không thể tránh khỏi chẳng kém gì sự gia tăng entropy
Với một số người, điều này nghe có vẻ hư vô và đầu hàng. Họ có thể nói chẳng phải ta nên chống lại entropy sao? Nhưng tôi nghĩ cách dùng thời gian tốt nhất và hiệu quả đầu tư cao nhất bắt đầu từ việc thừa nhận rằng đây không phải thứ để chiến đấu, mà là thứ để quản lý
Nếu lúc nào cũng giả định rằng mình có thể biết trạng thái của thế giới, bạn đang tự đặt lịch cho thất bại và tự trách mình. Cũng giống như 100% uptime không tồn tại, và với hầu hết mọi thứ thì đó cũng là mục tiêu sai
Nếu thừa nhận rằng mình đang quản lý một quá trình có mức độ bất định kiểu Heisenberg nhất định, bạn có thể chọn cách dùng quỹ thời gian giới hạn mỗi ngày sao cho tạo ra kết quả tốt hơn. Đặc biệt, bạn có thể đánh đổi thông minh giữa ứng phó trước và ứng phó sau, đồng thời hiểu rằng không thể đưa ứng phó sau về 0, và đôi khi bỏ ra 1 năm phòng ngừa chỉ để né 1 ngày xử lý hậu quả là điều vô lý
Vậy nên nên viết bao nhiêu tài liệu cho một commit? Nên có bao nhiêu tài liệu thiết kế hay kế hoạch kiểm thử? Tôi cũng không biết. Nhưng xin đưa ra một lựa chọn: mọi tài liệu đều được viết cho người đọc
Nếu bạn thay đổi codebase, các thành viên hiện tại của nhóm, kể cả người mới vào, phải có khả năng thông qua việc điều tra mà hiểu được thay đổi đó đã làm gì và vì sao nó được thực hiện, đồng thời có một vài cảnh báo về những chỗ đặt chân nguy hiểm hay những bug đang gánh tải
Điều này thường không có nghĩa là văn xuôi dài dòng, mà là các con trỏ tới ngữ cảnh bổ sung giúp dựng bối cảnh. Ví dụ như: “Bước này yêu cầu xác thực vì đây là một phần của chính sách mọi thay đổi đều cần phê duyệt đa bên. see: go/multiparty”
Những hệ thống theo đuổi sự hoàn hảo trong khi phải xử lý con người thực sự rất khó chịu. Ví dụ như DRM, trusted computing, remote attestation, Faro Plague, smart contracts
Một hệ thống có thể khởi động lại ở service mode để sửa sẽ tốt hơn nhiều. Vì ta không thể dự đoán phần mềm sẽ phải tiến hóa theo hướng nào để thực sự hữu ích cho con người trong tương lai. Thay vì khóa cứng 100%, tốt hơn là làm cho nó dễ sửa
Chúng tôi hầu như không viết thân commit, nhưng tiêu đề thì viết khá ổn. Nếu đó là thước đo thì tôi cũng chẳng rõ nó đang đo cái gì
Trong các codebase lớn, code rõ ràng và độ bao phủ kiểm thử đầy đủ thường hữu ích hơn tài liệu rất nhiều
Có những trường hợp một thay đổi hoàn toàn hợp lý được thực hiện và test cũng được cập nhật tương ứng, nhưng về sau nhìn lại thì hoàn toàn không rõ vì sao thay đổi đó lại cần thiết. Điều này càng đúng khi những dòng đã đổi gây ra hành vi ngoài dự kiến hoặc hành vi bổ sung trong môi trường production
Việc đơn giản revert có thể không phải là điều mong muốn, và lúc đó nếu có toàn bộ lịch sử về lý do thay đổi đã được thực hiện thì thật sự rất hữu ích
Tôi đã thấy rất nhiều trường hợp ý tưởng thì đúng nhưng lại sinh ra hệ quả không lường trước. Nếu biết được ý định ban đầu, ta có thể tìm ra thay đổi thực sự đúng đắn vừa sửa được vấn đề mới vừa giữ được lý do của thay đổi gốc
Nếu nhất quyết chỉ viết một dòng commit thì ít nhất cũng nên thêm số ticket để có thể đọc lịch sử ở đó
Tôi đã kiếm được kha khá tiền khi đi khắp những vùng đất xa lạ suốt 5 năm để cứu chữa các codebase kiểu này. @arp242, hãy luôn tăng giá và ngủ với https://archive.org/details/working-effectively-with-legacy-code dưới gối
May thay, các máy tạo đồ lặt vặt bằng AI lại viết commit message khổng lồ. Nhiều khi chúng cũng có liên quan phần nào đến thay đổi thực tế, vậy nên ít nhất khoản đó coi như được giải quyết