💡 Key Takeaways
- Why Most Git Workflows Fail Small Teams
- The Two-Branch Philosophy: Main and Feature
- Pull Requests: Your Quality Gate
- Commit Messages That Actually Help
Vào thứ Ba tuần trước, tôi đã chứng kiến một lập trình viên junior dành bốn mươi lăm phút để cố gắng tìm ra lý do tại sao nhánh tính năng của họ không thể merge. Thủ phạm? Quy trình Git quá phức tạp của đội chúng tôi, bao gồm bốn loại nhánh khác nhau, quy trình rebase bắt buộc và một chiến lược merge mà cần đến sơ đồ dòng chảy để hiểu. Tôi đã dẫn dắt các đội kỹ thuật trong mười hai năm, và tôi có thể nói với bạn một cách chắc chắn: phần lớn các đội nhỏ đang chìm trong sự phức tạp của Git mà họ không cần thiết.
💡 Những Điều Quan Trọng
- Tại Sao Phần Lớn Quy Trình Git Thất Bại với Các Đội Nhỏ
- Triết Lý Hai Nhánh: Chính và Tính Năng
- Yêu Cầu Kéo: Cửa Chất Lượng Của Bạn
- Tin Nhắn Commit Thực Sự Có Giá Trị
Tôi là Sarah Chen, và tôi đã dành một thập kỷ qua để xây dựng và mở rộng các đội phát triển tại ba startup khác nhau. Tôi đã thấy các đội gồm năm lập trình viên sử dụng quy trình được thiết kế cho các tổ chức có năm trăm kỹ sư. Tôi đã chứng kiến những lập trình viên xuất sắc lãng phí hàng giờ để điều hướng các chiến lược phân nhánh không mang lại giá trị cho công việc thực tế của họ. Và tôi đã học được rằng khi nói đến các quy trình Git cho các đội nhỏ—giả sử từ hai đến mười lập trình viên—đơn giản không chỉ là một điều tốt đẹp để có. Nó là sự khác biệt giữa việc phát hành tính năng và phát hành sự nhầm lẫn.
Đây là điều mà không ai nói với bạn: Git cực kỳ mạnh mẽ, điều đó có nghĩa là nó cũng cực kỳ dễ để trở nên phức tạp. Internet đầy các bài viết về Git-flow, GitHub flow, GitLab flow, phát triển dựa trên trunk, và hàng tá chiến lược khác. Phần lớn trong số chúng đang giải quyết những vấn đề bạn không gặp phải. Giờ đây, tôi sẽ chỉ cho bạn quy trình Git đã giữ cho các đội của tôi sản xuất, hạnh phúc và phát hành mã mà không có gánh nặng phức tạp của doanh nghiệp.
Tại Sao Phần Lớn Quy Trình Git Thất Bại với Các Đội Nhỏ
Trước khi chúng ta đi vào những gì hoạt động, hãy nói về những gì không hoạt động. Tôi đã kế thừa các mã nguồn từ các đội đang sử dụng Git-flow với nhánh phát triển, nhánh phát hành, nhánh sửa chữa nóng, và nhánh tính năng. Đối với một đội bốn lập trình viên làm việc trên một sản phẩm SaaS với các phát hành hàng tuần, điều này thực sự quá mức cần thiết.
Vấn đề với các quy trình phức tạp không phải là chúng sai—mà là chúng giải quyết các vấn đề xuất hiện khi quy mô lớn. Git-flow được tạo ra bởi Vincent Driessen vào năm 2010 cho một bối cảnh cụ thể: các đội quản lý nhiều phiên bản sản xuất đồng thời, với chu kỳ phát hành dài và nhu cầu quản lý sửa chữa nóng rộng rãi. Nếu bạn là một đội nhỏ phát hành liên tục vào một môi trường sản xuất duy nhất, bạn đang gánh chịu tất cả gánh nặng của Git-flow mà không nhận được bất kỳ lợi ích nào từ nó.
Tôi đã thực hiện một thí nghiệm vào năm ngoái với hai đội có kích thước và trình độ tương tự. Đội A đã sử dụng một quy trình đơn giản mà tôi sẽ mô tả. Đội B đã sử dụng Git-flow tiêu chuẩn. Trong ba tháng, Đội A đã phát hành nhiều tính năng hơn 23% và báo cáo mức độ thất vọng thấp hơn đáng kể trong khảo sát hàng quý của chúng tôi. Sự khác biệt không phải là tài năng hay nỗ lực—mà là ma sát. Mỗi loại nhánh bổ sung, mỗi bước merge thêm, mỗi thao tác rebase phức tạp đều tạo thêm tải nhận thức và làm chậm đội của bạn.
Các đội nhỏ có những ràng buộc khác so với các tổ chức lớn. Bạn có thể không có người quản lý phát hành chuyên biệt. Bạn có thể không cần hỗ trợ nhiều phiên bản sản xuất. Các lập trình viên của bạn đảm nhiệm nhiều vai trò và thường xuyên chuyển đổi ngữ cảnh. Quy trình của bạn nên phản ánh những thực tế này, không chống lại chúng. Khi tôi thấy một đội gồm năm người sử dụng cùng một chiến lược Git với Google, tôi biết họ đang tối ưu hóa cho những vấn đề mà hy vọng họ sẽ có vào một ngày nào đó thay vì những vấn đề mà họ đang gặp phải hôm nay.
Chi phí của sự phức tạp cộng dồn theo thời gian. Một lập trình viên dành mười phút mỗi ngày để điều hướng một quy trình Git không cần thiết phức tạp mất đi hơn bốn mươi giờ mỗi năm—một tuần làm việc hoàn toàn—chỉ để quản lý các nhánh. Nhân con số đó với đội của bạn, và bạn sẽ thấy hàng tuần mất đi năng suất hàng năm. Điều đó còn chưa tính đến thời gian dành để sửa các xung đột trong việc merge mà sẽ không tồn tại trong một quy trình đơn giản hơn, hoặc năng lượng tinh thần bị tiêu hao bởi việc liên tục nhớ nhánh nào cần có để merge vào nhánh nào.
Triết Lý Hai Nhánh: Chính và Tính Năng
Đây là quy trình đã hoạt động cho mọi đội nhỏ mà tôi đã dẫn dắt: hai loại nhánh, period. Bạn có nhánh chính (hoặc master, nếu bạn làm việc với các kho lưu trữ cũ), và bạn có nhánh tính năng. Chỉ vậy thôi. Không có nhánh phát triển, không có nhánh phát hành, không có nhánh sửa chữa nóng. Chỉ có chính và các tính năng.
Git cực kỳ mạnh mẽ, điều đó có nghĩa là nó cũng cực kỳ dễ để trở nên phức tạp. Phần lớn các đội đang giải quyết những vấn đề họ không gặp phải.
Nhánh chính của bạn đại diện cho sản xuất. Mọi thứ trong chính nên có thể triển khai bất cứ lúc nào. Điều này là không thể thương lượng. Nếu nhánh chính không thể triển khai, quy trình của bạn đã thất bại. Nguyên tắc duy nhất này loại bỏ một lượng lớn phức tạp vì nó có nghĩa là bạn luôn làm việc nhằm đạt được một mục tiêu rõ ràng: đưa mã của bạn vào trạng thái mà nó có thể merge một cách an toàn vào nhánh chính.
Các nhánh tính năng là nơi mọi công việc diễn ra. Bắt đầu một tính năng mới? Tạo một nhánh từ nhánh chính. Sửa một lỗi? Tạo một nhánh từ nhánh chính. Tái cấu trúc một số mã? Tạo một nhánh từ nhánh chính. Mô hình là nhất quán và dễ đoán. Không có cây quyết định về việc bạn nên tạo nhánh từ nhánh nào hay merge vào nhánh nào. Mỗi nhánh tính năng đều có quy trình sống giống nhau: nhánh từ chính, làm việc của bạn, merge lại vào chính.
Tôi đặt tên cho các nhánh tính năng theo một quy ước đơn giản: loại/mô tả ngắn. Ví dụ: tính năng/xác thực-người-dùng, sửa-bug/chuyển-hướng-dăng-nhập, tái-cấu-trúc/khách-hàng-api. Tiền tố loại làm cho nó ngay lập tức rõ ràng loại công việc nào đang diễn ra, và mô tả thì dễ đọc. Tôi đã thấy các đội sử dụng số ticket (tính năng/JIRA-1234), nhưng tôi thấy điều đó ít trực quan hơn khi bạn nhìn vào danh sách các nhánh. Bạn muốn có thể quét qua các nhánh của mình và ngay lập tức hiểu những gì đang được làm việc.
Sự đẹp đẽ của cách tiếp cận này là tính dễ đoán. Một lập trình viên mới gia nhập đội của bạn có thể hiểu toàn bộ quy trình Git của bạn trong khoảng năm phút. Không có sơ đồ phân nhánh phức tạp nào để ghi nhớ, không có trường hợp đặc biệt nào để nhớ. Nhánh từ chính, làm việc của bạn, merge vào chính. Sự đơn giản này có hiệu ứng tích lũy đối với năng suất. Khi quy trình của bạn đơn giản, các lập trình viên dành ít năng lượng tinh thần cho quy trình và nhiều hơn cho việc giải quyết các vấn đề thực sự.
Một câu hỏi mà tôi thường nhận được: thì những tính năng dài hạn mất vài tuần để hoàn thành thì sao? Bạn không cần một nhánh phát triển cho điều đó à? Không. Bạn cần các cờ tính năng. Nếu một tính năng chưa sẵn sàng cho người dùng, hãy ẩn nó sau một cờ. Điều này giữ cho mã của bạn được tích hợp liên tục trong khi vẫn cho bạn kiểm soát khi nào các tính năng trở nên khả dụng. Tôi đã thấy các đội tạo ra các chiến lược phân nhánh phức tạp để giải quyết các vấn đề mà các cờ tính năng giải quyết một cách thanh lịch hơn.
Yêu Cầu Kéo: Cửa Chất Lượng Của Bạn
Mỗi nhánh tính năng đều merge vào chính thông qua một yêu cầu kéo. Không có ngoại lệ. Tôi không quan tâm bạn là CTO hay chỉ là một thay đổi một dòng. Yêu cầu kéo không chỉ là về việc xem xét mã—chúng là về việc tạo ra một khoảnh khắc của quyết định có chủ ý trước khi mã vào sản xuất.
| Quy Trình | Kích Thước Đội | Độ Phức Tạp | Tốt Nhất Cho |
|---|---|---|---|
| Git-flow | 20+ lập trình viên | CAO | Nhiều phiên bản phát hành, phát hành theo lịch |
| GitHub Flow | 5-15 lập trình viên | THẤP | Triển khai liên tục, dự án đơn giản |
| Dựa trên Trunk | 10-50 lập trình viên | TRUNG BÌNH | Vòng lặp nhanh, cờ tính năng |
| Nhánh Tính Năng Đơn Giản | 2-10 lập trình viên | RẤT THẤP | Các đội nhỏ, phát hành hàng tuần, gánh nặng tối thiểu |
Đây là mẫu yêu cầu kéo của tôi, mà tôi đã tinh chỉnh qua nhiều năm thử nghiệm. Nó ngắn gọn vì các mẫu dài không được điền đầy đủ. Mỗi yêu cầu kéo phải bao gồm: mô tả ngắn gọn về những gì đã thay đổi, lý do thay đổi và cách kiểm tra nó. Chỉ vậy thôi. Ba phần, mỗi phần thường từ ba đến năm câu. Nếu bạn không thể giải thích thay đổi của mình trong không gian đó, có lẽ thay đổi của bạn quá lớn.
Đối với việc xem xét mã, tôi tuân theo một quy tắc đơn giản: mỗi yêu cầu kéo cần ít nhất một sự chấp thuận trước khi merge, nhưng người phê duyệt có trách nhiệm chia sẻ về bất kỳ vấn đề nào phát sinh. Điều này tạo ra cấu trúc động lực đúng đắn. Các người xem xét nhận thức được vai trò của họ vì họ không chỉ là con dấu cao su—họ đang đồng ký. Đồng thời, một sự chấp thuận là đủ vì chúng tôi là một đội nhỏ và chúng tôi tin tưởng lẫn nhau. Việc yêu cầu hai hoặc ba sự chấp thuận có thể hợp lý cho một đội năm mươi, nhưng với một đội năm người, chỉ khiến mọi thứ chậm lại.
Tôi đã thử nghiệm với các kỳ vọng thời gian phản hồi khác nhau cho việc xem xét mã. Điều gì hoạt động tốt nhất: các người xem nên cung cấp phản hồi trong vòng bốn giờ trong giờ làm việc. Không phải bốn giờ tập trung cho việc xem xét—bốn giờ để ít nhất thừa nhận yêu cầu kéo và cung cấp phản hồi ban đầu. Điều này giữ cho mã di chuyển mà không tạo ra kỳ vọng về phản hồi ngay lập tức. Nếu ai đó cần thêm thời gian cho một khám phá sâu sắc, họ sẽ bình luận nói vậy, và tác giả biết rằng họ sẽ nhận được phản hồi chi tiết sau đó.
Một thực tiễn đã cải thiện chất lượng mã của chúng tôi rất nhiều: yêu cầu tác giả yêu cầu kéo tự merge mã của họ sau khi được phê duyệt. Điều này có vẻ là một chi tiết nhỏ, nhưng nó thay đổi hành vi. Khi bạn biết bạn sẽ là người nhấp vào nút merge, bạn sẽ cẩn thận hơn về những thay đổi của mình. Bạn