Trong bài trước về chuyện viết phần mềm, một vấn đề mình đề cập là có rất nhiều người đang nhập môn về viết phần mềm. Có những bạn chỉ làm phần mềm vì cần giải quyết vấn đề của mình. Trên thế giới cũng có rất nhiều cá nhân như vậy, và kết quả là việc nở rộ những người làm phần mềm để giải quyết rất nhiều vấn đề khác nhau. Nhìn chung việc trăm hoa đua nở là một việc tốt, nhưng nó kéo theo một hệ quả xấu là mình có thể học cách làm từ người không biết làm.

Một trong những tật xấu bạn có thể gặp là việc học git như một công cụ học mót, học vì thấy ai cũng dùng: Với rất nhiều người, git chỉ là một công cụ để nhỡ không may ổ cứng hỏng thì mình có một copy trên cloud. Việc dùng git sai có thể không quan trọng với dự án có một người, nhưng khi dự án phát triển và phức tạp hơn, thì việc làm sai đấy có thể sẽ dẫn đến kết quả rất tai hại. Khi dự án trở nên lớn và phức tạp cần nhiều hơn một người nhúng tay vào làm, việc đọc hiểu được code của người khác (và chính mình) viết trong quá khứ là một việc rất quan trọng. Việc hiểu code quan trọng hơn gấp nhiều lần việc giải quyết được vấn đề một cách nhanh chóng. Bạn chắc chắn không thể nào có một doanh nghiệp, lab thành công khi tất cả mọi việc ở trong tay một người. Khi người này bị ốm, bị gãy chân, chuyển công ty là không ai biết chuyện gì đang diễn ra, không ai phát triển được thêm sửa được lỗi gì nữa. Đó là tình trạng sa lầy code.
Việc sa lầy này trong code cũng như trong thực tế đều rất tai hại. Ví dụ như việc dự án đường tàu điện ở Hà Đông không thể phát triển được, sa lầy trong nhiều năm. Vấn đề mình nghĩ ở đây không (đơn giản) chỉ là sự tham lam, mà còn là việc để việc vào trong tay người không biết làm và làm sai, làm rối rắm. Nó không chỉ tốn thời gian, tốn tiền, mà người sau động vào sửa một thành phần thì thành phần liên quan hỏng. Khi đó có cách duy nhất là đập đi làm lại từ đầu, như vậy còn khổ hơn rất nhiều việc phải làm từ đầu.
Khi học trường sau đại học, ông thầy mình có phát biểu câu thế này: "Nếu khi viết code phải động não một, thì khi sửa lỗi code phải động não gấp đôi ba lần." Nhưng đáng tiếc là thầy mình lúc đó không đưa ra lời khuyên để giải quyết vấn đề này. Nhiều khi mình ngồi nhìn vào code của mình viết một tháng liền không có tiến triển gì cả, vì không biết rõ là mình có sai không, sai thì sai chỗ nào, sửa thì sửa chỗ nào, đó là một cảm giác rất ngán ngẩm.
Để tránh được việc sa lầy này thì một cách hữu hiệu nhất là làm đúng ngay từ đầu.
Để làm đúng ngay từ đầu, với bất kể ngôn ngữ nào, khi bạn có phiên bản đầu tiên bắt đầu hơi có tiến triển thì bạn cần đưa vào git. Từ khi đưa vào git, mỗi thay đổi trong code người phát triển cần phải nắm được là tại sao có thay đổi đó, thay đổi đó để làm gì (nhưng không cần giải thích là làm cách nào, cái đó đã ở trong code của bạn).
Hãy cùng xem một commit tốt của dự án OpenWRT, một dự án phần mềm hệ điều hành nhúng cho các router wifi.
ath79: GL-AR750S (NOR/NAND): limit factory.img kernel size to 2 MB

The present U-Boot for GL-AR750S has a limit of 2 MB for kernel size.
While sysupgrade can manage kernels up to the present limit of 4 MB,
directly flashing a factory.img with a kernel size greater than 2 MB
through U-Boot will result in an unbootable device.

This commit uses the newly-introduced check-kernel-size build
operation to prevent the output of factory.img when the kernel
exceeds 2 MB in size, yet permits output of sysupgrade.img
as long as the kernel is within KERNEL_SIZE := 4096k

Cc: Chuanhong Guo 

Signed-off-by: Jeff Kletsky 

Chúng ta sẽ cùng tìm hiểu tại sao commit này tốt.

Dòng Subject

ath79: GL-AR750S (NOR/NAND): limit factory.img kernel size to 2 MB
Ở trên thì dòng subject gắn gọn (khi dùng git log --oneline), cho người đọc biết thay đổi phần nào của mã nguồn, thay đổi đó có hiệu ứng gì.
- ath79 là kiến trúc phần cứng mà OpenWRT hỗ trợ, thay đổi này ảnh hưởng đến kiến trúc này.
- GL-AR750S là thiết bị mà thay đổi này ảnh hưởng.
- "limit factory.img kernel size to 2 MB" là tóm tắt ảnh hưởng mà thay đổi này mang lại.
Nguyên tắc: Ngắn gọn, dùng thì chủ động, viết thành phần ảnh hưởng, ý chính của việc thay đổi trong một dòng cố gắng <72 ký tự.
Nguyên tắc: Nếu có hệ thống bug tracker thì refer tới ticket có liên quan.

Commit message

Ở phần tiếp theo (nội dung), người viết code giải thích tại sao việc này lại diễn ra. Thay đổi này diễn ra vì phần lõi hệ điều hành trong những phiên bản mới của OpenWRT đang dần phình to và tiệm cận đến 2MB, và khi nó vượt quá 2MB thì nếu nâng cấp từ firmware factory, thiết bị sẽ không boot, gây ra tình trạng cục gạch. Người này còn tiếp tục giải thích lý do người ta giới hạn kernel chỉ không làm ra file Factory nếu như kernel vượt quá 2MB, nhưng làm file Sysupgrade thì không sao cả, nên họ giới hạn kernel cho file sysupgrade ở 4MB.
Khi người về sau, chẳng hạn kernel phình to lên thật, có một build failure vì hệ thống build báo lỗi khi tạo ra file factory vì kernel quá lớn. Khi đọc code, rất có thể người khác (hoặc chính tác giả) sẽ không hiểu hay không nhớ tại sao với thiết bị GL-AR750S, thì file factory và file sysupgrade có hai giới hạn kernel khác nhau. Khi đó, người ta sẽ có xu hướng sửa lại cái gì thấy sai, tức là bỏ cái giới hạn đi để việc build hoàn tất. Nhưng khi người đó lục lại mã nguồn bằng lệnh "git blame file" rồi dùng "git show ," họ sẽ nhận ra ngay lý do này và không cố sửa để nó sai trở lại và tạo ra một firmware làm cho người cài vào thiết bị của họ biến router thành cục gạch.
Hãy tưởng tượng nếu bạn commit với message là "make it work for GL-AR750S," hoặc tệ hơn là "fix bugs." bạn sẽ không thể nào hiểu được có sự không nhất quán này và rất có thể người đi sau sẽ đi lầm bầm chửi người đi trước là tại sao dốt thế, và đi sửa lại để hậu quả đáng tiếc ở trên xảy ra.
Nguyên tắc: Không phải lúc nào cũng cần. Nhưng nếu cần viết, viết thay đổi cái gì, ảnh hưởng là gì, kết quả ra sao, tại sao bạn đi đến quyết định đó.

Nội dung code

Nội dung code quan trọng, nhưng mình nghĩ sẽ khó có thể nói trong một bài viết. Mình sẽ chỉ nói đến một việc: comment. Khi bạn học ở trường đại học, phần lớn thầy giáo sẽ nói với bạn rằng bạn cần phải comment code. Việc đó dẫn đến mình thấy có những người comment code như thế này:
// Holds the receive buffer, the buffer consists of elements of ints
int* buf;

// Allocate the buffer
buf = (int*) malloc (BUF_MAX_NUM_ELEMENTS * sizeof(int));

// Cleanup the buffer
free (buf);
Việc comment code như thế rất vô nghĩa và tốn thời gian vì đấy là việc hiển nhiên code của bạn đang làm, chúng ta không viết văn tả cảnh. Bạn không phải đi diễn tả chính xác cái diễn ra lù lù trước mắt.
Khi bạn comment code, bạn cần comment cái gì không hiển nhiên, không hợp logic, không dễ để nhận ra đại cục ngay. Hãy nhìn vào comment ở trong commit phía trên:
# NB: The kernel size is intentionally restricted at this time; see commit message
Việc giới hạn kích cỡ của file kernel là một việc không hiển nhiên, ngược đời. Bạn cần chú thích để nói tại sao nó lại ngược đời như thế. Như vậy đây là một comment tốt.
Nguyên tắc: Comment về giải thích về các thành phần liên quan, ý tưởng, căn nguyên của bạn chứ không giải thích hành động cụ thể. Comment cái gì không hiển nhiên, không dễ nhận ra.
Nguyên tắc: Đặt tên cho các variables hợp lý để không phải comment cái tên đó để làm gì.
Nguyên tắc: Nhất quán space-tab ngay từ đầu.

Mình nghĩ chỉ chừng đó ý tưởng thôi cũng đã làm cho dự án của bạn trông "pro" hơn rất nhiều rồi. Có lẽ việc này sẽ phải viết dài hơn nữa, nhưng mình nghĩ một việc chúng ta có thể làm là khi chắp code đầu năm, chúng ta sẽ cùng cố gắng làm cho code của mình tốt hơn, chất lượng cao hơn, để sánh vai với các cường quốc năm châu.