Ước lượng khi còn ẩn số, nói "không" và ưu tiên tech debt

4 câu hỏi~25 phútTừ khóa: estimate range, spike, work breakdown, trade-off, scope/time/quality, tech debt, prioritization

Tiếp đi: ước lượng. PM hỏi mình "migrate payment sang provider mới mất bao lâu?" mà mình chưa đọc docs provider đó. Mình nói đại "2 tuần" rồi trễ thành 5 tuần. Lần sau phải trả lời sao?

Chuyện này senior nào cũng gặp. Vấn đề không phải bạn ước lượng sai, mà là bạn đưa một con số chắc chắn cho một việc chưa chắc chắn.

1. Bài toán

PM: "Chuyển payment từ Provider A sang Provider B, bao lâu?"
Biết:     API A, code checkout hiện tại
Chưa biết: webhook B ra sao, sandbox B có ổn không,
           refund cũ (đơn trả bằng A) xử lý thế nào,
           team legal có cần review không

2. Vấn đề: vì sao "2 tuần" hỏng

3. Giải pháp: range + spike + breakdown

Công cụÝ nghĩa
RangeĐưa khoảng "3–6 tuần" kèm độ tự tin, không đưa một số
SpikeTimebox 2–3 ngày để khám phá ẩn số lớn nhất, sau đó ước lượng lại
BreakdownChia việc đến khi mỗi phần ≤ 2–3 ngày; phần nào không chia được là ẩn số
AssumptionsGhi rõ giả định; giả định sai thì ước lượng thay đổi
Re-estimateCập nhật khi biết thêm, báo sớm, không đợi đến hạn

4. Ví dụ và mẫu dùng ngay

Mẫu 1: Breakdown cho migrate payment

Việc                                   Ước lượng   Độ chắc
-------------------------------------  ----------  -------
Spike: sandbox B, webhook, refund       2-3 ngày    cao
Adapter PaymentGateway cho B            3-5 ngày    vừa
Webhook handler + idempotency           3-5 ngày    thấp (*)
Feature flag chuyển dần A → B           1-2 ngày    cao
Xử lý refund đơn cũ của A               2-8 ngày    thấp (*)
Contract test + E2E sandbox             3-4 ngày    vừa
Rollout 5% → 50% → 100% + theo dõi      3-5 ngày    vừa
-------------------------------------  ----------
Tổng                                    17-32 ngày  ≈ 4-6 tuần
(*) sẽ rõ sau spike

Mẫu 2: Câu trả lời cho PM

"Hiện tại em ước lượng 4–6 tuần, độ tự tin khoảng 60%.
 Hai ẩn số lớn: webhook của B và refund cho đơn cũ.
 Cho em 3 ngày spike, sau đó em đưa con số chắc hơn (±1 tuần).
 Nếu cần ra mắt sớm, mình có thể làm checkout mới trước,
 refund đơn cũ vẫn đi qua A thêm 1 tháng."

Câu cuối là trade-off: bạn không chỉ nói "lâu", bạn đưa lựa chọn.

Mẫu 3: Nói "không" (thật ra là "có, nếu…")

PM muốn thêm "mã giảm giá theo nhóm khách" vào sprint đang đầy:

Không nên: "Không kịp đâu anh."
Nên:       "Được anh. Sprint đang full với migrate payment.
            Mình có 3 lựa chọn:
            1. Làm coupon, lùi migrate 1 tuần (rủi ro: hợp đồng A hết hạn 30/11)
            2. Làm bản coupon đơn giản (chỉ % cố định), 2 ngày
            3. Để sprint sau
            Em nghiêng về 2. Anh chọn cái nào theo ưu tiên business?"

Tam giác cố định: scope / time / quality. Thêm scope thì phải bớt cái khác. Senior không hy sinh quality âm thầm (bỏ test), mà nói rõ trade-off để người có quyền business quyết định.

Mẫu 4: Ưu tiên tech debt vs feature

Đừng nói "code xấu, cần refactor". Nói bằng chi phí:

Tech debt: OrderService 2.500 dòng, không có test cho tính giá
Chi phí hiện tại:
  - 3/5 incident quý này liên quan tính giá
  - Mỗi feature giá mới mất thêm ~3 ngày vì sợ vỡ
Đề xuất: dành 20% mỗi sprint, 3 sprint
  - Sprint 1: characterization test cho PriceCalculator
  - Sprint 2: tách PricingService
  - Sprint 3: xóa code chết
Đo: số incident giá, lead time feature giá

Bảng chấm điểm nhanh để xếp backlog:

ItemImpact (1-5)Effort (1-5)Risk nếu bỏ quaƯu tiên
Fix double charge52Mất tiền, mất kháchNgay
Test cho tính giá43Incident lặp lạiSprint này (20%)
Coupon theo nhóm33ThấpBản đơn giản
Đổi sang Kotlin15KhôngKhông làm

5. Cách hoạt động: vòng ước lượng

Yêu cầu mơ hồ
     |
     v
Breakdown ──> phần nào không chia được? ──> Spike (timebox)
     |                                         |
     v                                         v
Range + giả định  <────────── cập nhật ───────┘
     |
     v
Commit theo milestone, theo dõi hàng tuần
     |
     v
Lệch > 20%? ──> báo PM ngay + đưa lựa chọn (cắt scope / lùi ngày)

6. So sánh

Tình huốngPhản xạ saiPhản xạ senior
Bị hỏi "bao lâu?"Một con số, đoánRange + giả định + spike
Sắp trễIm lặng, làm OTBáo sớm kèm phương án
Yêu cầu thêm"Không" hoặc "OK" rồi bỏ test"Có, nếu…" với 2–3 lựa chọn
Tech debt"Code xấu"Chi phí đo được + kế hoạch nhỏ

7. Khi nào nên / không nên

Story point với giờ thì nên dùng cái nào? Team mình cãi nhau mãi.

Cả hai đều dùng được, quan trọng là dùng cho đúng việc:

Trong phỏng vấn, đừng sa vào tranh luận công cụ. Nói ý chính: "ước lượng là để ra quyết định, nên em chọn đơn vị người nghe hiểu và luôn kèm độ tự tin."

Nếu PM vẫn ép "phải xong trong 2 tuần" dù mình đã giải thích thì sao?

Đúng rồi, chuyện này xảy ra thường xuyên. Deadline đôi khi là thật (hợp đồng, sự kiện Black Friday). Khi đó bạn cố định thời gian, linh hoạt scope:

"2 tuần thì em làm được:
  ✔ Checkout mới qua B cho thẻ Visa/Master
  ✔ Feature flag, rollback về A trong 1 phút
  ✘ Ví điện tử, refund đơn cũ → sprint sau
 Em gửi lại scope này qua Slack để mình thống nhất nhé."

Ghi lại bằng văn bản. Không phải để đổ lỗi sau này, mà để mọi người cùng hiểu một scope. Thứ duy nhất không cắt: test cho đường tiền và khả năng rollback.

Phỏng vấn hỏi "Bạn ước lượng thế nào khi yêu cầu chưa rõ?" và "Bạn cân bằng tech debt với feature ra sao?" thì trả lời sao?

Q1. Bạn ước lượng thế nào khi yêu cầu chưa rõ?

🇻🇳 Tiếng Việt:

Em chia việc nhỏ đến mức mỗi phần vài ngày, phần nào không chia được là ẩn số. Em đưa khoảng thời gian kèm giả định và độ tự tin, rồi đề xuất spike có timebox cho ẩn số lớn nhất. Ví dụ migrate payment provider, em báo 4–6 tuần, sau 3 ngày spike thu về 5 tuần ±1. Khi lệch, em báo sớm kèm lựa chọn cắt scope hoặc lùi ngày.

🇬🇧 English:

I break the work down until each piece is a few days; anything I can't break down is an unknown. I give a range with assumptions and a confidence level, and propose a time-boxed spike for the biggest unknown. For a payment provider migration I said 4 to 6 weeks, and after a 3-day spike narrowed it to 5 weeks plus or minus one. If we drift, I raise it early with options: cut scope or move the date.

Q2. Bạn cân bằng tech debt với feature ra sao?

🇻🇳 Tiếng Việt:

Em biến tech debt thành chi phí business: số incident, số ngày bị chậm mỗi feature. Sau đó đề xuất phần nhỏ, thường khoảng 20% mỗi sprint, gắn với khu vực sắp có feature mới để vừa trả nợ vừa ship. Em đo lại sau vài sprint để chứng minh nó đáng làm.

🇬🇧 English:

I translate tech debt into business cost: incidents and days lost per feature. Then I propose small chunks, usually around 20% of each sprint, focused on the area where upcoming features will land, so we pay debt and ship at the same time. I measure again after a few sprints to show it was worth it.

Q3. Bạn từng nói "không" với stakeholder chưa?

🇻🇳 Tiếng Việt:

Có, nhưng em thường nói "có, nếu…". Khi PM muốn thêm coupon vào sprint đầy, em đưa ba lựa chọn với chi phí của từng cái và đề xuất bản đơn giản. PM chọn, và scope được ghi lại rõ. Em không bao giờ âm thầm bỏ test để kịp.

🇬🇧 English:

Yes, but usually as "yes, if…". When the PM wanted coupons added to a full sprint, I laid out three options with the cost of each and recommended a simpler version. The PM chose, and we wrote the scope down. I never silently drop tests to hit a date.

Cách nhớ ngắn gọn


Tự kiểm tra

Vì sao không nên đưa một con số đơn lẻ?

Nó bị hiểu là cam kết, che mất mức độ không chắc chắn.

Spike là gì, kết quả của spike là gì?

Khám phá có timebox cho ẩn số lớn; kết quả là tài liệu ngắn/prototype và ước lượng chính xác hơn.

Deadline cố định thì linh hoạt cái gì?

Scope. Không cắt test đường tiền và khả năng rollback.

Cách trình bày tech debt với PM?

Bằng chi phí đo được (incident, ngày chậm) và kế hoạch nhỏ có đo lường.

Story point dùng để làm gì, không dùng để làm gì?

Lập kế hoạch sprint trong team; không đổi ra giờ, không so giữa các team.