- vừa được xem lúc

7 vấn đề thường gặp và giải pháp xây dựng Git Workflow

0 0 9

Người đăng: Solo Developer

Theo Viblo Asia

Git là gì?

Git là một hệ thống quản lý phiên bản (Version Control System), với chức năng chính là theo dõi, quản lý và tổ chức các phiên bản của code hoặc bất kì nhóm dữ liệu nào có nhu cầu thay đổi phát triển theo thời gian. Chức năng của git là vô cùng quan trọng trong việc hợp tác giữa các thành viên trong một team phát triển. Những hệ thống có tác dụng tương tự Git có thể kể đến như Bitbucket, SVN.

Tại sao cần tới git workflow và branching model hợp lý?

Có thể nói Git workflow là xương sống của một team phát triển, hầu hết các hoạt động của team đều gắn liền với git workflow và align với cách hoạt động của team. Do git chỉ là công cụ để quản lý version, nó không quy định bất cứ cách vận hành team hay tổ chức nào phải gắn liền. Teamlead hoặc CTO cần xác định cách vận hành của team mình, cùng với đó là mô hình các nhánh (Branching model) để phục vụ nhu cầu phát triển. Từ đó giảm thời gian quản lý code và các vấn đề gây xung đột có thể làm giảm performance của team. Từ khi CI/CD phát triển, tiến trình deploy ứng dụng có thể được tiến hành tự động ngay sau khi code được merge vào nhánh và ánh xạ tới môi trường cần thiết. Đây thực sự là một bước tiến vượt bậc nếu so sánh với việc build và deploy ứng dụng một cách manual như trước đây. Điều này dẫn đến các kịch bản CI/CD cần được thiết kế phù hợp với các môi trường, mục đích và nhánh hiện có. Mỗi tổ chức sẽ có cách đặt tên nhánh khác nhau, nhưng các process sẽ không nằm ngoài các cycle phát triển của team:

  • New feature/implement task : Developer thực thi các task (code) trên một nhánh độc lập, và môi trường thường là local.
  • Integration: Phase tích hợp, thường align với một môi trường hoàn chỉnh, các task branch sau khi được review sẽ được merge vào development branch để đưa vào tích hợp cùng hệ thống. Một tính năng sẽ chưa được nghiệm thu là CI deploy nếu nó không được tích hợp chung lên hệ thống. Đôi khi môi trường feature/task và Intergration có thể bị dùng chung với nhau do trong hệ thống có một số ứng dụng đặc thù, ứng dụng cloud... khiến việc phát triển hoàn toàn trên môi trường local là không thể.
  • Testing and fixbug : Sau khi đã tích hợp và hoạt động trên môi trường tích hợp, thường với ý nghĩa các task đã được tích hợp và hoạt động, các thay đổi này sẽ được chuyển sang môi trường test cho QA kiểm thử. Môi trường này cần tách biệt và được quản lý deployment phục vụ quá trình kiểm thử của QA.
  • User acceptance Testing - UAT : Sau khi pass qua phase kiểm thử và fix bug, code được merge sang nhánh UAT, tương đương với môi trường UAT. Môi trường này mang ý nghĩa là môi trường sát với production nhất, code đã được kiểm thử, không còn hoặc chỉ còn các issue nhỏ, sẵn sàng bàn giao cho khách hàng. User (người dùng cuối hoặc đại diện khách hàng) sẽ sử dụng môi trường này và confirm/đánh giá các tính năng đang được bàn giao. Nếu có bất cứ thay đổi hoặc sự không phù hợp nào sẽ được xác nhận từ môi trường này, tránh việc lên tới production mới phát hiện.
  • Release production: Sau khi vượt qua UAT, code được lưu trữ và triển khai lên môi trường production.

Trên đây là những phase phát triển chính của team, nhưng trên thực tế, code không đi theo một chiều như vậy. Sẽ luôn có khả năng code xảy ra lỗi trong bất cứ môi trường nào, bất kể là đang In QA hay Production. Vậy khi bắt đầu một sprint, code sẽ bắt đầu sinh ra từ đâu, được merge vào đâu sau mỗi phase phát triển? Và khi có phát sinh bug/issue trong phát triển và vận hành, developer cần làm như thế nào để đảm bảo các issue được fix mà không ảnh hưởng tới các phase hoặc sprint tiếp theo. Nhìn từ góc nhìn của developer, các công việc trải qua trong quá trình phát triển có liên quan đến git:

  • Phát triển feature/task
  • Fix bug trên môi trường QA test
  • Fix bug trên môi trường UAT
  • Fix bug trên môi trường production (hotfix)

"Về cơ bản cuộc sống của dev là tạo ra lỗi và đi sửa lỗi 🙂"

Những công việc này cần có code base để bắt đầu tiến hành code feature hoặc fixbug. Trước khi đi vào xây dựng một workflow đáp ứng các yêu cầu phát triển, ta cùng đi qua một số vấn đề liên quan đến Git mà developer có thể gặp phải.

Những issue có thể gặp phải trong quá trình phát triển

Backward compatible

Nhìn tổng thể trong một sprint, hầu hết quá trình là tiến trình đi lên song song giữa các nhánh code và môi trường theo thứ tự đã nêu ở các life cycle phát triển. Với phase implement task và integration, việc quản lý code khá thoải mái, develop có thể chủ động push code lên nhánh của cá nhân để lưu trữ hoặc request merge vào môi trường tích hợp để thực hiện dev test tính năng. Nhưng từ phase testing trở đi, code cần được quản lý chặt chẽ, vì thường các nhánh code này ánh xạ trực tiếp tới các môi trường ứng dụng đang vận hành. Việc merge code tự do sẽ làm ảnh hưởng tới downtime của ứng dụng hoặc development life cycle (do môi trường đang được sử dụng bởi team khác, vd QA đang verify bug).Với happy case, mọi thứ sẽ đi lên dần theo các nhánh và môi trường. Nhưng điều gì sẽ xảy ra khi có issue xảy ra trên môi trường Testing/UAT/Production. Issue cần triển khai fix theo nguyên lý: Issue xảy ra ở môi trường nào, code fix phải đảm bảo có mặt trên môi trường đó và cả các môi trường phía trước. Điều này đảm bảo code trên các môi trường runtime luôn được cập nhật các bản vá cho các tính năng, không gây rủi ro cho các tính năng khác và hệ thống. Lấy ví dụ nếu bug xảy ra ở UAT trên một tính năng đang phát triển, nó cần được fix và merge cả vào Integration/Testing/UAT, còn các nhánh feature của cá nhân có thể định kì pull các thay đổi từ nhánh đã base code.

Kiểm thử nhiều phiên bản code trên cùng một môi trường

Thông thường một team chỉ có 2-3 môi trường để phục vụ mục đích phát triển, điều này dẫn đến việc đôi khi developer cần thực hiện tích hợp hoặc kiểm thử tính năng khác nhau trên cùng một môi trường. Các tính năng này có thể có những khác biệt lớn khiến chúng không thể tích hợp cùng nhau, mà cần được kiểm thử độc lập mà không làm ảnh hưởng đến member khác. Lúc này cần có kịch bản để thực hiện việc triển khai các tính năng khác nhau lên cùng một môi trường. Việc chuyển đổi qua lại giữa các nhánh tính năng khác nhau trên cùng môi trường không khó, nhưng nó thường gây ra những xung đột về mặt dữ liệu hoặc việc giao tiếp thông báo phiên bản nào đang on air không xuyên suốt giữa team dev và QA. Nhất là với hệ thống microservice được tạo thành từ nhiều dịch vụ nhỏ.

Remove một tính năng ra khỏi sprint

Không phải tất cả các sprint đều diễn ra suôn sẻ theo lý tưởng: manager cùng với member estimate một cách hợp lý, các requirement được làm rõ ràng. Tình trạng các task bị chậm tiến độ có thể đến từ rất nhiều nguyên nhân, lúc này thì team sẽ ứng xử như thế nào: Giải trình để xin giãn deadline, OT cho kịp deadline, hay giữ nguyên deadline và chấp nhập skip qua một vài task. Với các option giãn deadline và OT sẽ không ảnh hưởng tới cách vận hành mà chỉ ảnh hưởng đến vấn đề thời gian. Nhưng việc skip một tính năng đã được merge vào môi trường Test hoặc UAT sẽ cần được thực hiện theo nguyên tắc giảm đối đa sự ảnh hưởng của việc remove code ra khỏi nhánh Việc này có thể ảnh hưởng tới code của member khác và effort của QA đã test những tính năng trước đó. Tưởng tượng môi trường Test lúc này là một mớ các feature/bugfig, việc comment code không phải là phương án đảm bảo (nếu không muốn nói là tệ 😃. Phương án hợp lý sẽ là remove các commit chứa feature/task đó ra khỏi nhánh hiện tại.

Multiple repository or Mono Repository

Xét trên khía cạnh quản lý code của một team khi phát triển ứng dụng, với việc áp dụng kiến trúc microservice, số lượng các thành phần trong hệ thống trở lên tương đối lớn. Có thể có từ vài service đến hàng chục service chỉ xét riêng cho phần code business. Việc lưu trữ code theo hướng Multiple và Mono sẽ mang lại những ưu nhược điểm khác nhau. Về cơ bản việc lưu trữ multiple repo có nhiều ưu điểm hơn về mặt isolation các repo, giảm độ phụ thuộc về code và cải thiện thời gian build do target vào đúng repo cần build lại. Nhưng nhược điểm sẽ đến từ việc thao tác và quản lý version code qua nhiều repo. Cần có phương án cho việc quản lý version code hợp lý để sẵn sàng cho việc rollback hoặc tracing khi cần thiết. Mono repository thì ở chiều hướng ngược lại, nếu hệ thống không có quá nhiều member và scope phát triển không lớn, việc tập trung code vào cùng một repo sẽ giúp đơn giản hóa về mặt quản lý và thao tác. Các thay đổi giữa tất cả các sub project cũng được đồng bộ với nhau trong cùng một commit, cùng một nhánh. Lúc này một nhánh đã bao hàm tất cả các chức năng của ứng dụng.

Quản lý version code và gắn tag

Trong quá trình CICD,khi deploy một ứng dụng, sẽ có một mã đi kèm để làm định danh (name + version) cho bản artifact được build. Bản artifact này có thể được lưu trữ trực tiếp hoặc đóng gói vào trong image để phục vụ cho phase deploy. Với các bản build thông thường cho mục đích dev/test/QA thì định danh này không quá quan trọng, có thể sử dụng các mã random hoặc commit id. Nhưng với các nhánh code đã ổn định về tính năng, các bản build (artifact) hoặc image sẽ cần gắn một mã có chủ đích (version/tag) với mục đích lưu trữ lâu dài và quản lý được các phiên bản build và phiên bản code phục vụ cho rollback / track change/ specific version... Version này cũng nên được đồng bộ vào trong code, tại các file metadata của các ngôn ngữ phát triển khác nhau (pom.xml, package.json...). Lúc này khi bạn nhìn từ bất cứ view nào từ code, file build hay runtime cũng có thể biết được phiên bản hiện tại của code/ứng dụng. Vậy nên gắn tag cho các version như thế nào? Dễ thấy việc gắn nhãn cho các phiên bản không bó buộc theo một công thức nào cả. Một cách gắn nhãn phổ biến trên các repo mà ta thấy trên github là Semantic Versioning với công thức đơn giản là một dãy 3 số đại diện cho mức độ thay đổi của mỗi phiên bản <MAJOR.MINOR.PATCH> và một số hậu tố khác đại diện cho độ ổn định. Xem thêm về Semantic Versioning tại đây Ngoài ra còn có một số strategy đặt tên version phổ biến khác:

  • Sequential numbering: Phiên bản theo thứ tự tăng dần
  • Date base: Gắn nhãn liên quan đến khoảng thời gian phát triển hoặc release (I like this one 😃)

Commit convention và tích hợp git với các ứng dụng CICD khác

Có một câu chuyện vui về commit code như thế này.

*Quy trình xảy ra khi có chuông báo cháy trong một công ty IT: Git add => Git commit => Git push => Ra khỏi tòa nhà. *

Đây chỉ là trong trường hợp khẩn cấp, trên thực tế chúng ta cần commit với các message có nghĩa, ít nhất member khác có thể track lại commit đó có ý nghĩa gì mà không cần phải vọc vào code. Cách thực hiện commit này được gọi là Commit convention. Một quy ước chung của team và tổ chức nhằm thống nhất cách đặt tên để tiện quản lý và phục vụ các nhu cầu CICD tích hợp. Rất nhiều ứng dụng trong quy trình CICD sử dụng commit convention để trigger các sự kiện một cách tự động. Như việc generate change log tự động sau mỗi release, tự động chuyển trạng thái của task trên Jira sau khi commit được deploy... Tất cả những việc này đều phụ thuộc vào commit convention, mà chủ yếu nằm ở việc viết message khi commit. Điểm qua một số commit:

  • git commit: Commit không có message, ngay cả người thực hiện khi nhìn lại lịch sử thay đổi cũng ko biết commit này thực hiện gì
  • git commit -m"Support login 2FA": khá hơn commit trước là đã có thể biết được commit này phục vụ chức chăng Two factor authentication
  • git commit -m"fix: bug mail not send 2FA": Biết thêm được đây là commit phục vụ fix bug
  • git commit -m"fix:[JIRA-ID] Support login 2FA, bug mail not send": Commit fixbug theo task số <JIRA-ID> Có thể thấy Commit convention là một quy ước quan trọng trong phát triển phần mềm. Nó thể ảnh hưởng đến performance khi thực hiện phát triển hoặc cả khi maintain cần tracing lại lịch sử các commit và tính năng, thậm chí là keypoint liên kết quá trình CICD.

Conflict do format code

Vấn đề này thường ít được để ý khi người quản lý setup workflow và convention cho team. Cùng nhìn vào một trường hợp mà mình đã gặp phải trong một dự án vài năm trước. Team phát triển backend sử dụng Java Spring, lúc đó người thì dùng Eclipse, người thì Netbean, người thì Intellij. Lúc đầu chia mỗi người một nghiệp vụ, mọi người không code chung trong một file nên không có vấn đề gì, sau một thời gian ở một file mà người cũ đã code khoảng 2000 dòng, một member mới code cùng file đó, và rất tự nhiên, khi code xong người đó format code cả file với phím tắt và commit lên. Lúc này theo lịch sử thay đổi git không chỉ còn là phần code của người đó mới thêm vào mà là gần như toàn bộ file do khác biệt về format code. Nếu hai người cùng checkout từ một commit, sau đó format code với hai format khác nhau trên cùng một file, lúc này việc merge lại sẽ xảy ra conflict trên phạm vi rất lớn, mặc dù thao tác đều đúng. Liệu có thể chỉ format đoạn code mình đã viết, không việc đó rất bất tiện, và cũng không thể buộc member sử dụng cùng một IDE. Vậy giải pháp nào cho việc đồng bộ format code giữa các IDE khác nhau mà thuận tiện nhất cho developer, tránh xảy ra tình trạng đã nêu ở trên.

Các giải pháp cho git workflow

Bản thân mình đã trải qua những issue của git khá nhiều lần. Những lần mất code làm mất thời gian trace lại lịch sử, hay việc conflict code quá nhiều khiến mất nhiều thời gian ngồi resolve conflict, hay việc phải OT để fix bug do đã lỡ merge một tính năng vào mà không biết remove nó một cách an toàn ra sao... Nên từ đó team đã họp và tìm các phương án để xác định workflow hiệu quả. Một Git workflow hiệu quả là workflow không chỉ giảm được các thao tác của developer, giảm rủi ro do con người, cùng với đó là đảm bảo sự hợp tác giữa các role phát triển không xảy ra xung đột trong một quy trình phát triển. Nó còn cần tối ưu về mặt thao tác, productivity nhất. Cùng xem xét một số option cho việc xây dựng mô hình git workflow.

Ứng dụng một số git workflow build in

  • Centralized workflow: Mọi người đều làm việc xung quanh nhánh main, cũng là nhánh trung tâm duy nhất của hệ thống. Các member đều có một nhánh main trên local remote tới nhánh main trên git server. Khi có các thay đổi, mỗi member sẽ pull nhánh main về, resolve conflict và sau đó tự push code của mình lên nhánh main. Dễ thấy workflow này có nhiều nhược điểm về mặt collaboration giữa các member.
  • Feature Branching: Ý tưởng ở đây là mỗi feature/task nên có cho riêng mình một nhánh thay vì code tất cả mọi thứ trên cùng một nhánh. Cách này giúp các member có thể phát triển feature mà không làm ảnh hưởng đến nhánh code base hoặc code của member khác
  • Gitflow Workflow: Flow này được thiết kế với định hướng xoay quanh và bám sát các phase phát triển của project release: feature/fixbug/hotfix... Workflow này là một giải pháp out-of-box, có cả một extention của git (git-flow) hỗ trợ workflow này. Về cơ bản người dùng sẽ setup các nhánh của mình align với các nhánh theo concept của gitflow. Sau đó thực hiện đúng các bước khi thao tác với mỗi kiểu task (new feature, fixbug, hotfix...). Lúc này extention sẽ tối giản hóa thao tác của người dùng nhưng vẫn đảm bảo code được merge vào đúng nơi cần đến theo mỗi kiểu task.

Đọc thêm về Git Workflow tại đây

Tự define và implement cho phù hợp

Từ việc tham khảo các workflow có sẵn cộng với những tổng kết về các issue thường gặp với git trong quá trình làm việc, các bạn có thể xây dựng nên Git workflow cho team mình, bao gồm cả xây dựng quy trình và các convention làm việc cần thiết. Cho đến hiện tại tôi cảm thấy git workflow của mình khá phù hợp cho việc phát triển theo agile và giải quyết được hầu hết các issue một cách thuận tiện cho dev. Vì việc setup này khá dài nên xin hẹn các bạn ở một bài viết khác.

Kết

Đến đây chúng ta đã hiểu tầm quan trọng của một git workflow hiệu quả ảnh hưởng tới performance và quy trình phát triển ứng dụng như thế nào. Hi vọng bài viết này có thể giúp các bạn hiểu thêm về git workflow và các issue về git mà các bạn đang gặp phải. Ở bài viết sau chúng ta sẽ xây dựng một workflow đáp ứng quy trình phát triển Agile và CI/CD một cách chi tiết nhất.

Đón đọc: Setup git workflow full option in Real Life

Bình luận

Bài viết tương tự

- vừa được xem lúc

Đặt tên commit message sao cho "tình nghĩa anh em chắc chắn bền lâu"????

. Lời mở đầu. .

1 1 768

- vừa được xem lúc

Tập hợp những câu lệnh GIT hữu dụng

Dưới đây là một vài ví dụ về các câu lệnh Git mà tôi thường dùng. git config --global user.name "John Doe". git config --global user.

0 0 68

- vừa được xem lúc

Cấu hình CI/CD với Github (phần 2): Trigger một work flow

Events trigger. Bạn có thể cấu hình cho workflows chạy khi có một sự kiện nào đó xảy ra trên GitHub, theo một lịch có sẵn hoặc cũng có thể là một sự kiện nào đó xảy ra ngoài GitHub.

0 0 80

- vừa được xem lúc

Cấu hình CI/CD với Github (phần 1): Một ít lý thuyết

CI/CD là gì. Về mặt khái niệm là vậy nhưng về mặt triển khai thì CI/CD là quá trình tự động thực hiện các quá trình build, test, release, deploy khi có các trigger như commit/merge code lên một branch định sẵn hoặc có thể là tự động chạy theo một lịch cố định.

0 0 128

- vừa được xem lúc

Giới thiệu về Git LFS

. Git LFS là gì . Git LFS làm điều này bằng cách thay thế các tệp lớn trong repo của bạn bằng một con trỏ nhỏ.

0 0 37

- vừa được xem lúc

Git workflow được Google và Facebook sử dụng có gì hay ho

Với developer thì Git hẳn là công cụ rất quen thuộc và không thể thiếu rồi. Thế nhưng có mấy ai thực sự hiểu được Git.

0 0 85