Version Control & CI/CD: Từ "Lưu file" đến "Triển khai tự động"

Hãy tưởng tượng bạn đang code một feature mới, đến 90% thì... máy bị tắt nguồn đột ngột. Hoặc bạn vừa merge code của đồng nghiệp, và đột nhiên toàn bộ hệ thống báo lỗi không rõ nguyên nhân. Hay đơn giản hơn: bạn cần biết ai đã sửa dòng code này 3 tháng trước và tại sao họ lại làm vậy?

Đây chính là lý do Version Control (Quản lý phiên bản) và CI/CD (Tích hợp và Triển khai Liên tục) trở thành kỹ năng bắt buộc của mọi kỹ sư phần mềm. Không phải để "sang chảnh", mà để sống sót trong môi trường làm việc nhóm và production thực tế.

Git: Nhiều hơn là "Ctrl+S trên Cloud"

Hiểu Git qua Directed Acyclic Graph (DAG)

Nhiều người nghĩ Git chỉ là công cụ backup code lên cloud. Thực tế, Git là một distributed version control system với cấu trúc dữ liệu tinh vi: DAG - Directed Acyclic Graph.

Vậy DAG là gì?

  • Directed: Mỗi commit có hướng - trỏ đến parent commit(s) của nó
  • Acyclic: Không có vòng lặp - không bao giờ có commit A → B → C → A
  • Graph: Mạng lưới các commit có thể phân nhánh và hợp nhất
          E---F---G (feature-branch)
         /
A---B---C---D (main)
         \
          H---I (hotfix-branch)

Mỗi commit là một snapshot đầy đủ của codebase tại thời điểm đó, không chỉ là "diff" so với version trước. Đây là điểm khác biệt lớn giữa Git và các hệ thống cũ như SVN.

Tại sao điều này quan trọng?

  1. Branching "free": Tạo nhánh mới không tốn nhiều tài nguyên vì chỉ là pointer
  2. Merging thông minh: Git biết chính xác ancestor chung để merge hiệu quả
  3. History immutable: Một khi commit, bạn không thể thay đổi quá khứ (chỉ có thể tạo commit mới sửa lỗi)

Git Internals: Những gì ẩn sau git commit

Ba vùng lưu trữ của Git:

  1. Working Directory: Nơi bạn chỉnh sửa file
  2. Staging Area (Index): Khu vực "chuẩn bị" cho commit tiếp theo
  3. Repository (.git folder): Lưu trữ toàn bộ lịch sử

Workflow cơ bản:

Working Directory  →  git add  →  Staging Area  →  git commit  →  Repository

Tại sao cần Staging Area?

Nhiều người thắc mắc: "Tại sao không commit trực tiếp mà phải git add trước?"

→ Staging Area cho phép bạn kiểm soát chính xác những gì được commit:

  • Bạn sửa 5 file, nhưng chỉ muốn commit 3 file liên quan đến feature A
  • Bạn có thể git add -p để chọn từng hunk (đoạn code) trong một file
  • Tránh commit nhầm debug code, console.log(), hoặc credentials

Ví dụ thực tế:

# Bạn đang fix bug và đồng thời refactor code
git add src/fix-payment-bug.js    # Chỉ add file fix bug
git commit -m "Fix payment calculation error"

git add src/refactor-utils.js      # Commit refactor riêng
git commit -m "Refactor: extract common utilities"

Chiến lược Phân nhánh: GitFlow vs Trunk-based Development

GitFlow: Kiến trúc phân nhánh rõ ràng

GitFlow được Vincent Driessen giới thiệu năm 2010, phù hợp với release-based development (phát hành theo từng version).

Các nhánh trong GitFlow:

  1. main/master: Production-ready code. Mỗi commit ở đây là một release
  2. develop: Nhánh tích hợp cho lần release tiếp theo
  3. feature/xxx: Phát triển tính năng mới (branch từ develop)
  4. release/x.y.z: Chuẩn bị release (branch từ develop)
  5. hotfix/xxx: Sửa bug khẩn cấp trên production (branch từ main)

Workflow:

1. Tạo feature branch từ develop
   git checkout -b feature/user-authentication develop

2. Code và commit
   git commit -am "Implement login API"

3. Merge về develop
   git checkout develop
   git merge --no-ff feature/user-authentication

4. Khi sẵn sàng release, tạo release branch
   git checkout -b release/1.2.0 develop

5. Bug fixes trên release branch, sau đó merge vào main và develop
   git checkout main
   git merge --no-ff release/1.2.0
   git tag -a v1.2.0

Ưu điểm:

  • Rõ ràng, dễ hiểu cho team lớn
  • Hỗ trợ nhiều version song song (VD: maintain v1.x trong khi phát triển v2.x)
  • Phù hợp với software phải release theo lịch (mobile apps, on-premise software)

Nhược điểm:

  • Phức tạp, nhiều nhánh cần quản lý
  • Merge conflicts tăng cao nếu feature branch sống quá lâu
  • Chậm - không phù hợp với continuous deployment

Trunk-based Development: "Ship fast, fix faster"

Trunk-based là chiến lược của các công ty tech lớn (Google, Facebook) và startup cần tốc độ.

Nguyên tắc cốt lõi:

  1. Tất cả developer commit trực tiếp vào main/trunk (hoặc feature branch sống < 1 ngày)
  2. Main branch luôn luôn deployable
  3. Sử dụng Feature Flags để ẩn/hiện tính năng chưa hoàn thiện

Workflow:

1. Code trực tiếp trên main (hoặc short-lived branch)
   git checkout main
   git pull
   git checkout -b feature/quick-fix

2. Commit nhỏ, thường xuyên
   git commit -m "Add user model"
   git commit -m "Add user controller"

3. Merge về main trong vòng vài giờ/1 ngày
   git checkout main
   git merge feature/quick-fix
   git push origin main

Feature Flags ẩn code chưa xong:

if (featureFlags.newCheckout) {
  // Code mới, chưa stable
  return <NewCheckoutFlow />;
} else {
  // Code cũ, đã stable
  return <OldCheckoutFlow />;
}

Khi feature mới ready, chỉ cần flip flag từ falsetrue, không cần deploy lại code.

Ưu điểm:

  • Cực kỳ nhanh - deploy nhiều lần/ngày
  • Ít merge conflicts vì code được integrate liên tục
  • Khuyến khích viết code modular, testable

Nhược điểm:

  • Yêu cầu kỷ luật cao (test kỹ trước khi commit)
  • Cần CI/CD tốt để catch lỗi sớm
  • Feature Flags phức tạp nếu quản lý nhiều flags

So sánh và Lựa chọn

Tiêu chí GitFlow Trunk-based
Phù hợp cho Release-based, team lớn Continuous delivery, startup
Tốc độ release Chậm (vài tuần/tháng) Nhanh (nhiều lần/ngày)
Complexity Cao Thấp
Merge conflicts Nhiều Ít
Yêu cầu CI/CD Trung bình Cao

Rule of thumb:

  • Mobile apps, game, enterprise software → GitFlow
  • Web apps, SaaS, microservices → Trunk-based

Code Review: "Nghệ thuật" phản biện mang tính xây dựng

Code review không chỉ là "tìm bug" - đó là cơ hội knowledge sharing, maintain code quality, và mentor junior developers.

Quy trình Code Review với Pull/Merge Request

1. Developer tạo PR (Pull Request)

git checkout -b feature/add-payment-gateway
# ... code code code ...
git push origin feature/add-payment-gateway
# Tạo PR trên GitHub/GitLab

2. PR phải include:

  • Description rõ ràng: "What" (thay đổi gì), "Why" (tại sao), "How" (cách implement)
  • Screenshots/GIFs nếu thay đổi UI
  • Link đến ticket/issue liên quan
  • Self-review: Reviewer tự đọc code của mình trước khi gửi

3. Reviewer checklist:

Functionality: Code có làm đúng yêu cầu không?
Tests: Có unit/integration tests đầy đủ không?
Edge cases: Xử lý các trường hợp ngoại lệ chưa?
Performance: Có query N+1, memory leak, hoặc inefficient algorithms không?
Security: Có lỗ hổng SQL injection, XSS, hardcoded secrets không?
Code style: Tuân thủ coding conventions của team không?
Naming: Biến/hàm có tên dễ hiểu không? (getUserById tốt hơn getData)
Documentation: Code phức tạp có comment giải thích không?

Code Review Best Practices

Cho Reviewer:

Nitpick rõ ràng: Nếu comment không critical, tag [nit] để author biết có thể bỏ qua

[nit] Có thể đổi tên biến `temp` thành `filteredUsers` cho dễ hiểu hơn

Khen ngợi code tốt: Không chỉ chỉ ra lỗi

Nice refactoring! Việc extract helper function này làm code clean hơn nhiều 👍

Hỏi thay vì ra lệnh:

❌ "Đổi logic này lại"
✅ "Bạn có cân nhắc cách tiếp cận X không? Nó có thể handle edge case Y tốt hơn"

Chỉ ra vấn đề + đề xuất giải pháp:

Issue: Function này có cyclomatic complexity cao (15), khó test
Suggestion: Có thể extract validation logic ra một function riêng?

Cho Author:

Không defensive: Code review không phải tấn công cá nhân
Giải thích reasoning: Nếu reviewer không hiểu, có thể code chưa đủ clear
Tách commit nhỏ: Một PR khổng lồ 50 files = nightmare cho reviewer
Respond mọi comment: Ngay cả khi chỉ là "Fixed" hoặc "Good point"

Git Commit Messages: "Văn bản" của lịch sử

Một commit message tốt giúp team hiểu tại sao thay đổi này xảy ra, không chỉ cái gì thay đổi (code diff đã show rồi).

Conventional Commits format:

<type>(<scope>): <subject>

<body>

<footer>

Types phổ biến:

  • feat: Tính năng mới
  • fix: Sửa bug
  • refactor: Sửa code không thay đổi behavior
  • docs: Cập nhật documentation
  • test: Thêm/sửa tests
  • chore: Công việc maintenance (update dependencies, v.v.)

Ví dụ tốt:

feat(auth): add OAuth 2.0 social login

- Integrate Google and Facebook OAuth providers
- Add user profile sync from social accounts
- Store OAuth tokens securely in encrypted format

Closes #123

Ví dụ tệ:

update code          (quá mơ hồ)
fix bug              (bug nào?)
asdfasdf             (vô nghĩa)

CI/CD: Từ Code đến Production trong vài phút

Continuous Integration (CI)

Mục tiêu: Phát hiện lỗi sớm nhất có thể bằng cách integrate code thường xuyên và test tự động.

CI Pipeline điển hình:

1. Developer push code
   ↓
2. CI server (Jenkins/GitHub Actions) trigger
   ↓
3. Checkout code
   ↓
4. Install dependencies (npm install, pip install)
   ↓
5. Run linter (eslint, pylint)
   ↓
6. Run unit tests
   ↓
7. Run integration tests
   ↓
8. Build application (compile, bundle)
   ↓
9. Success ✅ hoặc Fail ❌

GitHub Actions example:

name: CI Pipeline

on: [push, pull_request]

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      - name: Setup Node.js
        uses: actions/setup-node@v2
        with:
          node-version: '18'
      - run: npm install
      - run: npm run lint
      - run: npm test
      - run: npm run build

Lợi ích CI:

  • Catch lỗi trước khi merge vào main
  • Đảm bảo code mới không phá vỡ existing functionality
  • Enforce coding standards (qua linter)
  • Tự động hóa manual testing

Continuous Deployment (CD)

Mục tiêu: Deploy code lên production tự động sau khi pass tất cả tests.

CD Pipeline:

1. CI pipeline pass ✅
   ↓
2. Build Docker image
   ↓
3. Push image lên registry
   ↓
4. Deploy lên staging environment
   ↓
5. Run smoke tests (kiểm tra các chức năng core)
   ↓
6. Deploy lên production (hoặc chờ manual approval)

Deployment Strategies:

1. Blue-Green Deployment

  • Có 2 môi trường giống hệt nhau: Blue (hiện tại) và Green (mới)
  • Deploy code mới lên Green, test kỹ
  • Chuyển traffic từ Blue sang Green (zero downtime)
  • Nếu có lỗi, rollback bằng cách switch lại Blue

2. Canary Deployment

  • Deploy version mới cho 5% users trước
  • Monitor metrics (error rate, latency)
  • Nếu OK, tăng dần lên 25% → 50% → 100%
  • Nếu có vấn đề, rollback chỉ ảnh hưởng 5% users

3. Rolling Deployment

  • Cập nhật từng server một (trong cluster)
  • Server 1 → chờ healthy → Server 2 → ...
  • Đơn giản nhưng có short downtime

CI/CD Best Practices

Fail fast: Nếu linter fail, đừng chạy test nữa (waste time)
Parallel jobs: Chạy unit test và integration test song song để giảm thời gian
Caching dependencies: Cache node_modules, venv để không phải install lại mỗi lần
Secrets management: Không bao giờ hardcode API keys - dùng environment variables
Notifications: Slack/Email thông báo khi pipeline fail
Rollback plan: Luôn có chiến lược rollback nếu deployment fail

Tổng kết: Git và CI/CD trong thực tế

Git không chỉ là "save file trên cloud", mà là nền tảng cho collaboration và code history management. Hiểu DAG, branching strategies, và code review giúp bạn làm việc hiệu quả trong team.

CI/CD biến việc deploy từ "nghi lễ phức tạp" thành quy trình tự động. Bạn focus vào viết code, còn lại để CI/CD lo.

Skill progression:

  1. Junior: Biết git add, commit, push, pull
  2. Mid: Hiểu branching, merge conflicts, rebase, code review
  3. Senior: Thiết kế branching strategy cho team, optimize CI/CD pipeline, mentor juniors

Action items:

  • Thực hành GitFlow hoặc Trunk-based trong dự án cá nhân
  • Setup CI pipeline đơn giản với GitHub Actions
  • Tham gia code review với mindset "học hỏi" thay vì "tìm lỗi"

Trong bài tiếp theo, chúng ta sẽ đi sâu vào Machine Learning Concepts - nơi những nguyên lý toán học gặp gỡ thực tiễn ứng dụng AI.


Bài viết thuộc series "From Zero to AI Engineer" - Module 3: Implementation & Quality Assurance