Chuyển tới nội dung chính

03 - Conflict Resolution

Mục tiêu: Xử lý conflict từng bước, biến conflict thành cơ hội

Thời lượng: 30 phút Đối tượng: Developer, Tech Lead


🎯 Mindset: Conflict là cơ hội, không phải vấn đề

Conflict xảy ra

= Hai người cùng sửa một chỗ

= Hai người cùng quan tâm đến code này

= CƠ HỘI để:
- Review architecture
- Improve communication
- Học cách code của nhau
- Tối ưu design

❌ WRONG Mindset:

Conflict = Phiền phức
→ Giải quyết cho xong
→ Giữ code của mình
→ Hoặc giữ code người khác
→ KHÔNG hiểu ý đồ

✅ RIGHT Mindset:

Conflict = Signal
→ Architecture cần review?
→ Communication cần improve?
→ Refactor cần thiết?
→ Ngồi cùng nhau, học hỏi

🚨 Quy tắc BẮT BUỘC

1. KHÔNG TỰ Ý RESOLVE

# ❌ CẤM làm này:
git merge dev
# CONFLICT in src/auth.js

# Tự ý chọn code của mình
git checkout --ours src/auth.js
git add src/auth.js
git commit -m "Resolve conflict"

# HOẶC tự ý chọn code người khác
git checkout --theirs src/auth.js

Tại sao CẤM?

  • Không hiểu ý đồ code người khác
  • Có thể break feature người khác
  • Bỏ lỡ cơ hội refactor tốt hơn

2. BẮT BUỘC: Ngồi cùng nhau

Conflict xảy ra

1. Xác định: Ai viết code conflict?
2. Liên hệ: Slack/Zoom/F2F
3. Hẹn: 15-30 phút ngồi cùng
4. Cùng resolve:
- Hiểu ý đồ của cả hai
- Thảo luận approach tốt nhất
- Cùng code giải pháp
- Cùng test
5. Document: Ghi decision vào commit message

3. KHÔNG Auto-Accept Theirs/Ours

# ❌ Các lệnh này CẤM dùng khi conflict:
git merge --strategy-option theirs
git merge --strategy-option ours
git checkout --theirs .
git checkout --ours .

# ✅ CHỈ accept từng dòng code sau khi HIỂU
# (Dùng merge tool, resolve manual)

🔍 Step-by-Step Conflict Resolution

Step 1: Identify Conflict

# Scenario: Bạn đang làm feature/auth-policy
git checkout feature/auth-policy
git rebase dev

# Output:
# CONFLICT (content): Merge conflict in src/auth/AuthService.js
# CONFLICT (content): Merge conflict in src/auth/PolicyEngine.js

Step 2: Examine Conflicts

# Xem files bị conflict
git status

# Output:
# Unmerged paths:
# both modified: src/auth/AuthService.js
# both modified: src/auth/PolicyEngine.js

# Xem chi tiết conflict
cat src/auth/AuthService.js

Conflict markers:

<<<<<<< HEAD (Your changes)
class AuthService {
authenticate(user, policy) {
return this.policyEngine.evaluate(user, policy);
}
}
=======
class AuthService {
authenticate(user) {
return this.validateCredentials(user);
}
}
>>>>>>> dev (Their changes)

Step 3: Find Who Wrote Conflicting Code

# Tìm commit gây conflict
git log dev --oneline -- src/auth/AuthService.js

# Output:
# a1b2c3d feat: Update auth to use credentials only (@bob)
# e4f5g6h refactor: Extract auth logic (@alice)

# Xem chi tiết commit
git show a1b2c3d

# Xem blame (ai viết dòng nào)
git blame src/auth/AuthService.js

Kết quả:

Người viết: @bob
Commit: a1b2c3d
Thời gian: 2 days ago
Mục đích: Simplify auth, remove policy dependency

Step 4: Contact & Schedule

# Message to @bob (Slack/Teams):

Hi @bob,

I'm working on `feature/auth-policy` and got a conflict
in `src/auth/AuthService.js` with your recent commit a1b2c3d.

Could we sit together for 15-30 min to resolve this?
I want to understand your approach and see how we can
integrate both changes properly.

My approach: Adding policy-based auth
Your approach: Simplified credentials-only auth

Available: Today 2-4pm, Tomorrow 9-11am

Step 5: Collaborative Resolution

Meeting Agenda (15-30 min)

1. Hiểu ý đồ (5 min)
- @bob explains: Tại sao remove policy?
- @you explains: Tại sao add policy?

2. Discussion (10 min)
- Cả hai approaches có value?
- Có thể kết hợp?
- Refactor cần thiết?
- Trade-offs?

3. Decision (5 min)
- Pick approach
- OR: Design new approach kết hợp cả hai
- OR: Refactor to support both

4. Code together (10 min)
- Resolve conflict
- Test
- Commit

Example Resolution

Scenario:

  • @bob removed policy vì "too complex, không ai dùng"
  • @you added policy vì "client ABC cần custom auth rules"

Discussion:

@bob: Hiện tại chỉ 1 client cần policy, 5 clients khác dùng simple auth
@you: Đúng, nhưng roadmap có 3 clients mới cần policy

Decision: Support CẢ HAI
→ Simple auth = default
→ Policy auth = optional (via config)

Resolution code:

// Kết hợp cả hai approaches
class AuthService {
constructor(config) {
this.config = config;
this.policyEngine = config.enablePolicy
? new PolicyEngine(config.policyRules)
: null;
}

authenticate(user, credentials) {
// @bob's simple auth (default)
const isValid = this.validateCredentials(user, credentials);

// @you's policy auth (optional)
if (this.policyEngine) {
return isValid && this.policyEngine.evaluate(user);
}

return isValid;
}
}

Test both scenarios:

// Test simple auth (existing clients)
const simpleAuth = new AuthService({ enablePolicy: false });
expect(simpleAuth.authenticate(user, creds)).toBe(true);

// Test policy auth (new client ABC)
const policyAuth = new AuthService({
enablePolicy: true,
policyRules: [...]
});
expect(policyAuth.authenticate(user, creds)).toBe(true);

Step 6: Resolve & Commit

# Edit file với solution đã thống nhất
code src/auth/AuthService.js

# Remove conflict markers
# Implement agreed solution

# Test
npm test

# Mark as resolved
git add src/auth/AuthService.js

# Continue rebase
git rebase --continue

# Commit message (DOCUMENT decision)
git commit -m "feat: Support both simple and policy-based auth

Resolved conflict with @bob's commit a1b2c3d.

Decision:
- Keep simple auth as default (for existing 5 clients)
- Add optional policy-based auth (for client ABC + 3 upcoming)
- Config-driven: enablePolicy flag

Trade-offs considered:
- Complexity vs Flexibility → Choose flexibility
- Performance impact → Minimal (lazy load policy engine)

Tested:
- Simple auth: All existing tests pass
- Policy auth: New tests added

Discussed with: @bob
Date: 2024-12-21"

🛠️ Conflict Resolution Tools

When conflict occurs:
→ VS Code highlights conflicts
→ Options:
- Accept Current Change (yours)
- Accept Incoming Change (theirs)
- Accept Both Changes
- Compare Changes

⚠️ DON'T just click "Accept"
✅ Read BOTH changes, understand context
✅ Discuss with author
✅ Then manually integrate

Git Merge Tool

# Configure merge tool (one-time setup)
git config --global merge.tool vscode
git config --global mergetool.vscode.cmd 'code --wait $MERGED'

# When conflict, launch merge tool
git mergetool

# This opens VS Code with 3-way merge view:
# - LEFT: Your changes
# - RIGHT: Their changes
# - CENTER: Result (edit this)

Command Line (Manual)

# View conflict
cat src/file.js

# Edit manually
vim src/file.js

# Remove conflict markers:
# <<<<<<< HEAD
# =======
# >>>>>>> branch

# Save resolved version

# Mark resolved
git add src/file.js

📋 Conflict Types & Solutions

Type 1: Simple Text Conflict

Conflict:

<<<<<<< HEAD
const API_URL = 'https://api.staging.example.com';
=======
const API_URL = 'https://api.prod.example.com';
>>>>>>> dev

Resolution:

// Config-driven instead of hard-code
const API_URL = process.env.API_URL || 'https://api.staging.example.com';

Lesson: Avoid hard-coding environment-specific values


Type 2: Logic Conflict

Conflict:

<<<<<<< HEAD (your feature)
function calculateDiscount(user) {
if (user.isPremium) {
return user.orderTotal * 0.2; // 20% for premium
}
return 0;
}
=======
function calculateDiscount(user) {
if (user.loyaltyPoints > 1000) {
return user.orderTotal * 0.15; // 15% for loyal customers
}
return 0;
}
>>>>>>> dev (their feature)

Resolution:

// Support BOTH features
function calculateDiscount(user) {
let discount = 0;

// Premium discount (your feature)
if (user.isPremium) {
discount = Math.max(discount, user.orderTotal * 0.2);
}

// Loyalty discount (their feature)
if (user.loyaltyPoints > 1000) {
discount = Math.max(discount, user.orderTotal * 0.15);
}

// Note: User gets BEST discount (not cumulative)
return discount;
}

Lesson: Design flexible logic that accommodates multiple rules


Type 3: Refactor Conflict

Conflict:

<<<<<<< HEAD (your refactor)
// You extracted to separate file
import { validateUser } from './validators';

function login(user) {
validateUser(user);
// ...
}
=======
function login(user) {
// They added new validation inline
if (!user.email || !user.password) {
throw new Error('Invalid credentials');
}
if (user.isBlocked) {
throw new Error('User is blocked');
}
// ...
}
>>>>>>> dev (their addition)

Resolution:

// Move new validation to extracted file
import { validateUser } from './validators';

function login(user) {
validateUser(user); // Now includes isBlocked check
// ...
}

// In validators.js
export function validateUser(user) {
if (!user.email || !user.password) {
throw new Error('Invalid credentials');
}
// NEW: Add their validation
if (user.isBlocked) {
throw new Error('User is blocked');
}
}

Lesson: Continue refactoring pattern, incorporate new logic


Type 4: File Rename Conflict

Scenario:

  • You: Renamed AuthService.jsAuthenticationService.js
  • Them: Modified AuthService.js content

Git behavior:

Git may NOT detect rename
→ Shows as: deleted AuthService.js + added AuthenticationService.js
→ Their changes to AuthService.js appear as conflict

Resolution:

# 1. Accept your rename
git rm src/auth/AuthService.js

# 2. Apply their changes to renamed file
git show <their-commit-hash>:src/auth/AuthService.js > temp.js
# Manually merge their changes into AuthenticationService.js

# 3. Commit
git add src/auth/AuthenticationService.js
git commit -m "Merge rename + changes from @author

- Renamed AuthService → AuthenticationService
- Applied their changes: <describe changes>
"

🔁 Common Conflict Scenarios

Scenario 1: Merge dev → feature branch

# Update feature branch with latest dev
git checkout feature/your-branch
git merge dev

# CONFLICT!

# Resolve (với collaboration)
# ...

# Complete merge
git add .
git commit -m "Merge dev into feature/your-branch

Conflicts resolved in:
- src/file1.js (discussed with @bob)
- src/file2.js (accepted their refactor)
"

Scenario 2: Rebase feature branch on dev

# Cleaner history with rebase
git checkout feature/your-branch
git rebase dev

# CONFLICT in commit abc123

# Resolve conflict
# ...

# Continue rebase
git add .
git rebase --continue

# If multiple conflicts
# Resolve each commit one by one
# git rebase --continue after each

# When done
git push origin feature/your-branch --force-with-lease

Rebase vs Merge:

Rebase:
✅ Clean linear history
✅ Easy to review
❌ Rewrites history (need force push)
❌ More conflicts (resolve per commit)

Merge:
✅ Preserves history
✅ Single conflict resolution
❌ Messy history with merge commits
✅ Safer (no force push)

Scenario 3: Multiple files conflict

git rebase dev

# CONFLICT in 5 files!
# src/auth/AuthService.js
# src/auth/PolicyEngine.js
# src/config/auth.config.js
# src/types/auth.types.js
# tests/auth.test.js

# DON'T panic!

# 1. Triage
# Which files are related?
# → All auth-related

# 2. Resolve in order
# Start with types (foundation)
git add src/types/auth.types.js

# Then config
git add src/config/auth.config.js

# Then core logic
git add src/auth/PolicyEngine.js
git add src/auth/AuthService.js

# Finally tests
git add tests/auth.test.js

# 3. Continue
git rebase --continue

Scenario 4: Conflict during cherry-pick

# Scenario: Want one commit from another branch
git cherry-pick abc123

# CONFLICT!

# Resolve
# ...

# Continue
git add .
git cherry-pick --continue

# Or abort if too complex
git cherry-pick --abort

🚫 What NOT to Do

❌ Auto-resolve without understanding

# DON'T:
git merge dev
# Conflicts...
git checkout --ours . # Keep all my changes
git add .
git commit -m "Resolve conflicts"

# WHY BAD:
# - Lose their changes completely
# - May break their feature
# - No learning opportunity

❌ Resolve conflicts alone (complex cases)

# DON'T:
# Complex conflict in core auth logic
# → Spend 2 hours figuring out their intent
# → Guess and resolve

# DO:
# → Spend 20 min discussing with author
# → Understand intent immediately
# → Resolve correctly in 10 min

❌ Ignore conflicts and commit broken code

# DON'T:
git add .
git commit -m "Fix conflicts"
# (But code doesn't compile or tests fail)

# DO:
# After resolving:
npm test # ✅ All tests pass
npm run lint # ✅ No lint errors
npm run build # ✅ Build succeeds
# THEN commit

❌ Blame others for conflicts

❌ "Their code caused conflicts"
❌ "They should have checked before merging"
❌ "This is their fault"

✅ "We both touched same area - let's align"
✅ "Good opportunity to review architecture"
✅ "Let's make sure both features work well"

✅ Conflict Prevention

1. Communicate Early

# In Slack/Teams before coding:

"Planning to refactor AuthService to support policies.
Will touch:
- src/auth/AuthService.js
- src/auth/PolicyEngine.js (new)
- tests/auth/*.test.js

Anyone working on auth code this sprint?"

Effect: Others aware → coordinate → avoid conflicts


2. Small, Frequent PRs

❌ BAD:
1 PR với 50 files changed (3 weeks work)
→ High conflict probability
→ Hard to review
→ Merge nightmare

✅ GOOD:
5 PRs, each 10 files (merge each week)
→ Low conflict probability
→ Easy to review
→ Smooth merges

3. Sync Often

# Every morning
git checkout dev
git pull origin dev

git checkout feature/your-branch
git rebase dev # or git merge dev

# Before lunch
git pull origin dev --rebase

# Before end of day
git pull origin dev --rebase

Effect: Catch conflicts early when small


4. Modular Code

// ❌ BAD: Everyone edits God class
class Application {
// 2000 lines
// Auth, DB, API, UI, everything
}

// ✅ GOOD: Separate concerns
class AuthService { }
class DatabaseService { }
class APIService { }
class UIManager { }

Effect: Different people work on different files


5. Code Review Culture

Fast PR reviews
→ Code merged quickly
→ Less time for conflicts to develop
→ Smaller merge windows

Slow PR reviews
→ Code sits in PR for days
→ Meanwhile dev branch moves ahead
→ Huge conflicts when finally merge

Best practice: Review PRs within 24 hours


📋 Conflict Resolution Checklist

When conflict happens:

- [ ] Don't panic
- [ ] Identify conflicting files
- [ ] Find who wrote conflicting code (git blame/log)
- [ ] Contact them (Slack/F2F)
- [ ] Schedule 15-30 min meeting
- [ ] Discuss both approaches
- [ ] Understand intent of both changes
- [ ] Decide resolution strategy together
- [ ] Code resolution together (or solo after agreement)
- [ ] Test thoroughly (both features work)
- [ ] Document decision in commit message
- [ ] Tag collaborator in commit message
- [ ] Mark resolved and commit
- [ ] Push & continue

🎓 Learning from Conflicts

After resolving conflict, ask:

1. Architecture question:
- Why did we conflict?
- Is code too coupled?
- Should we refactor for modularity?

2. Communication question:
- Could we have known earlier?
- Do we need better coordination?
- Should we have design review before coding?

3. Process question:
- Are PRs too large?
- Do we sync often enough?
- Is code review fast enough?

4. Positive outcome:
- What did I learn from their approach?
- Is the merged solution better than both originals?
- Can we apply this pattern elsewhere?

📊 Conflict Metrics (For Improvement)

Track over time:

- Conflicts per sprint
- Time to resolve conflicts (should decrease)
- Conflicts requiring rework (should decrease)
- Conflicts leading to improvements (should increase)

Healthy trend:

Sprint 1: 15 conflicts, avg 2h resolution, 5 reworks
Sprint 5: 8 conflicts, avg 30min resolution, 1 rework
Sprint 10: 3 conflicts, avg 20min resolution, 0 reworks
→ Better communication, modular code, frequent sync

🔙 Quay lại: 00-README.md