Tại Sao Hầu Hết Các Quy Trình Git Thất Bại Với Các Nhà Phát Triển Độc Lập
Vấn đề với hầu hết các bài hướng dẫn và quy trình Git là chúng được viết bởi những người làm việc tại các công ty có nhiều đội ngũ, quy trình xem xét mã, và các đường dẫn triển khai liên quan đến ít nhất ba môi trường khác nhau. Khi bạn là một nhà phát triển độc lập, bạn không có những ràng buộc đó—nhưng bạn cũng không có những mạng lưới an toàn đó. GitFlow, quy trình thống trị trong những năm 2010, là một ví dụ hoàn hảo. Nó được thiết kế bởi Vincent Driessen cho một vấn đề cụ thể: quản lý các phiên bản cho phần mềm cần hỗ trợ nhiều phiên bản đồng thời. Nếu bạn đang xây dựng một ứng dụng máy tính để bàn mà khách hàng cài đặt tại chỗ, GitFlow thật hợp lý. Nếu bạn là một nhà phát triển độc lập phát hành một sản phẩm SaaS hoặc một trang web khách hàng, nó thật sự là thừa thãi. Hành trình điển hình của một nhà phát triển độc lập với Git diễn ra như thế này: Bạn bắt đầu chỉ với việc cam kết vào `main` (hoặc `master`, tùy thuộc vào khi nào bạn học Git). Sau đó bạn đọc một bài viết về "các quy trình Git chuyên nghiệp" và cảm thấy tội lỗi. Bạn thực hiện các nhánh tính năng. Sau đó bạn thêm một nhánh `develop` vì đó là những gì sơ đồ đã chỉ ra. Chẳng bao lâu, bạn đang dành 20 phút mỗi ngày chỉ để quản lý các nhánh, và bạn thậm chí không chắc tại sao một nửa trong số đó tồn tại. Tôi đã trải qua điều đó. Tôi đã có những kho lưu trữ với các nhánh mang tên `feature/new-feature`, `feature/new-feature-2`, `feature/new-feature-actually-final`, và `feature/new-feature-for-real-this-time`. Nếu bạn chưa bao giờ gặp khủng hoảng về đặt tên nhánh, bạn hoặc là đang nói dối, hoặc là bạn chưa phát triển độc lập đủ lâu. Vấn đề cơ bản là các quy trình đội nhóm được thiết kế để giải quyết các vấn đề đội nhóm: phối hợp công việc giữa nhiều người, ngăn chặn xung đột, quản lý quy trình xem xét mã, và duy trì sự ổn định trong các môi trường chung. Khi bạn làm việc một mình, hầu hết các vấn đề này đơn giản là không tồn tại. Bạn không thể có một xung đột gộp với người khác nếu không có ai khác.Quy Tắc Ba Cam Kết Đã Thay Đổi Mọi Thứ
Sau thảm họa 2 giờ sáng của tôi, tôi bắt đầu phân tích các mô hình làm việc thực tế của mình. Tôi đã xem lại lịch sử Git của mình trong sáu tháng trước đó và nhìn vào từng nhánh tôi đã tạo, từng lần gộp tôi đã thực hiện, và từng xung đột tôi đã giải quyết. Những gì tôi tìm ra thật sáng tỏ. Chín mươi phần trăm các nhánh tính năng của tôi chứa ba cam kết trở xuống trước khi gộp. Đây không phải là những tính năng phức tạp, kéo dài nhiều tuần cần tách biệt. Chúng là những cải tiến nhỏ, sửa lỗi, và thay đổi từng bước mà tôi đã tách biệt nhân tạo thành các nhánh vì tôi nghĩ đó là những gì "các nhà phát triển thực thụ" làm. Mười phần trăm còn lại—những tính năng phức tạp thực sự—mới là nơi mà các nhánh có ý nghĩa. Nhưng ngay cả lúc đó, tôi nhận thấy một điều: những nhánh gây ra vấn đề là những nhánh tôi để mở hơn một tuần. Nhánh sống càng lâu, càng có khả năng gây ra sự cố khi gộp lại. Điều này dẫn tôi đến những gì tôi gọi là Quy Tắc Ba Cam Kết: Nếu một thay đổi mất hơn ba cam kết, nó có thể quá lớn và nên được chia nhỏ. Nếu không thể chia nhỏ, đó là một trong những trường hợp hiếm hoi mà một nhánh thật sự hữu ích. Quy tắc này buộc tôi phải suy nghĩ khác đi về cách tôi làm việc. Thay vì tạo một nhánh cho "thiết kế lại toàn bộ giao diện người dùng," tôi sẽ tạo một nhánh cho "cập nhật kiểu nút" hoặc "triển khai thành phần điều hướng mới." Mỗi nhánh tồn tại tối đa một hoặc hai ngày, chứa những thay đổi tập trung và gộp sạch sẽ. Sự thay đổi tâm lý thật sự đáng kể. Tôi ngừng suy nghĩ về "tính năng" và bắt đầu suy nghĩ về "những gia tăng có thể triển khai." Mỗi nhánh phải đại diện cho điều gì đó mà tôi có thể phát hành lên sản xuất mà không làm hỏng chức năng hiện có. Điều này tự nhiên giữ cho các nhánh nhỏ và ngắn hạn.Ngày Tôi Phát Hành Một Lỗi Lên Sản Xuất (Và Tại Sao Nó Làm Quy Trình Của Tôi Tốt Hơn)
Hãy để tôi kể cho bạn về lần triển khai tệ nhất trong sự nghiệp của tôi. Tôi đang làm việc trên một dự án khách hàng—một hệ thống đặt phòng cho một chuỗi khách sạn nhỏ. Tôi đã làm việc trên một nhánh tính năng trong hai tuần (vâng, tôi biết, tôi đã phá vỡ quy tắc của chính mình) và thêm một tích hợp thanh toán mới. Nhánh đã phân nhánh đáng kể so với `main`. Tôi đã quá tập trung vào tính năng thanh toán mà không theo kịp các sửa chữa nhỏ và cải tiến mà tôi đã thực hiện trực tiếp trên `main` cho các yêu cầu khẩn cấp của khách hàng. Khi đến lúc gộp, tôi gặp xung đột trong 14 tệp tin. Tôi đã giải quyết các xung đột, chạy các bài kiểm tra (chúng đã vượt), và triển khai lên sản xuất. Chỉ trong vòng một giờ, tôi nhận được một cuộc gọi hoảng loạn từ khách hàng. Mẫu đặt phòng đã hỏng. Không phải tích hợp thanh toán mới—mẫu đặt phòng cơ bản đã hoạt động tốt trong nhiều tháng. Chuyện gì đã xảy ra? Trong khi giải quyết một trong các xung đột gộp, tôi đã vô tình giữ phiên bản sai của một hàm. Các bài kiểm tra không phát hiện ra nó vì tôi chưa viết các bài kiểm tra cho trường hợp cạnh đó (bài học rút ra). Vấn đề hoàn toàn là lỗi của tôi, nhưng quy trình đã làm cho việc đó dễ dàng để phạm phải sai lầm. Sự kiện đó đã dạy tôi một điều quan trọng: là một nhà phát triển độc lập, rủi ro lớn nhất của tôi không phải là mã của người khác—đó là mã của chính tôi từ hai tuần trước. Khi tôi chuyển đổi giữa các phần khác nhau của một dự án, tôi thực sự đang hợp tác với các phiên bản quá khứ của chính mình. Và phiên bản cũ thường là một đồng nghiệp không đáng tin cậy. Sự nhận thức này dẫn đến cột trụ thứ hai của quy trình của tôi: Quy Tắc Một Tuần. Không có nhánh nào sống lâu hơn một tuần. Nếu một tính năng sẽ mất lâu hơn thế, tôi chia nhỏ nó thành các phần nhỏ hơn mà có thể hoàn thành và gộp trong một tuần. Nếu điều đó không khả thi, tôi sử dụng các cờ tính năng để gộp mã chưa hoàn chỉnh vào `main` trong khi giữ cho nó ẩn khỏi người dùng. Quy Tắc Một Tuần đã cứu tôi vô số lần. Nó buộc tôi phải luôn gần với `main`, có nghĩa là tôi luôn làm việc với phiên bản mới nhất của mã nguồn. Nó ngăn chặn loại phân kỳ dẫn đến các xung đột gộp phức tạp. Và nó giữ cho tôi trung thực về phạm vi—nếu tôi không thể hoàn thành điều gì đó trong một tuần, có lẽ tôi đang cố làm quá nhiều cùng một lúc.Chi Phí Thực Sự Của Độ Phức Tạp Của Nhánh
Hãy nói về con số. Tôi đã theo dõi các hoạt động liên quan đến Git của mình trong ba tháng trước và sau khi đơn giản hóa quy trình của mình. Các kết quả thật rõ ràng:| Hoạt động | Trước (giờ/tuần) | Sau (giờ/tuần) | Thời gian Tiết kiệm |
|---|---|---|---|
| Tạo và quản lý các nhánh | 2.5 | 0.5 | 2 giờ |
| Giải quyết các xung đột gộp | 3.0 | 0.3 | 2.7 giờ |
| Quyết định làm việc trên nhánh nào | 1.5 | 0.1 | 1.4 giờ |
| Làm sạch các nhánh cũ | 1.0 | 0.1 | 0.9 giờ |
| Rebase và đồng bộ hóa các nhánh | 2.0 | 0.2 | 1.8 giờ |
| Tổng chi phí phụ về Git | 10.0 | 1.2 | 8.8 giờ |
Tại Sao "Cam Kết Sớm, Cam Kết Thường Xuyên" Là Sai Lầm Đối Với Các Nhà Phát Triển Độc Lập
Đây là một quan điểm gây tranh cãi: lời khuyên "cam kết sớm, cam kết thường xuyên" thực sự có hại cho các nhà phát triển độc lập. Tôi biết điều này trái ngược với trí tuệ thông thường, nhưng hãy nghe tôi nói.Mục đích của các cam kết không phải là tạo ra một nhật ký chi tiết của mọi phím bấm. Mà là để tạo ra những điểm kiểm tra có ý nghĩa kể một câu chuyện về cách mã của bạn đã phát triển. Khi bạn làm việc một mình, bạn là người duy nhất cần phải hiểu điều đó.