Published on

Day 17 - Refactor legacy code an toàn

Authors

1. Mục tiêu bài học

Sau khoảng 2 giờ, học viên có thể:

  • Dùng Claude Code để khám phá legacy module ở chế độ read-only trước khi sửa.
  • Viết characterization tests để khóa behavior hiện tại.
  • Refactor theo lát cắt nhỏ, có test và rollback sau từng bước.
  • Áp dụng Strangler Fig pattern khi cần thay dần implementation cũ bằng implementation mới.
  • Nhận diện lúc Claude Code đang refactor quá rộng và biết dừng lại.
  • Ghi guardrails vào CLAUDE.md hoặc .claude/rules/ để team dùng lại.

2. Bối cảnh thực tế

Trong taskflow-ai, giả sử có module src/legacy/legacyTaskSummary.ts xử lý thống kê task. Module này có một function dài trộn parsing, business rule và format output. Không ai dám sửa vì không chắc client đang phụ thuộc behavior nào.

Claude Code có thể đọc nhanh và đề xuất rewrite toàn bộ. Với legacy code, đó là hướng rủi ro. Việc đầu tiên không phải làm code đẹp hơn, mà là khóa behavior hiện tại bằng test. Sau đó mới tách từng phần nhỏ.

Không nên dùng Claude Code để refactor legacy khi:

  • Chưa có test hoặc chưa biết public API.
  • Diff dự kiến chạm nhiều module cùng lúc.
  • Có production data, secret hoặc customer payload trong fixture.
  • Team chưa review được rollback plan.

3. Kiến thức nền

Refactor theo lát cắt nhỏ

Một lát cắt tốt chỉ đổi một ý tưởng: extract helper, tách formatter, đổi tên biến, hoặc thêm adapter. Test phải pass trước và sau lát cắt.

Thứ tự khuyến nghị:

observe -> characterization tests -> small refactor -> targeted test -> review diff -> repeat

Không trộn behavior change với structural refactor. Nếu phát hiện behavior cũ là bug, ghi lại thành task riêng.

Characterization test

Characterization test mô tả behavior đang tồn tại, kể cả behavior chưa đẹp. Mục tiêu là biết refactor có làm đổi output hay không.

Ví dụ:

Input: task due yesterday, status TODO
Expected hiện tại: overdue = 1

Nếu behavior có vẻ kỳ lạ, thêm comment Characterization: khóa behavior hiện tại, chưa khẳng định đây là behavior đúng.

Strangler Fig pattern

Khi legacy module lớn, đừng rewrite một lần. Tạo facade hoặc adapter để caller cũ vẫn gọi API cũ, sau đó chuyển từng case sang implementation mới.

caller -> legacy facade -> legacy implementation
caller -> legacy facade -> new implementation for migrated cases

4. Step-by-step thực hành

Trong phần này, mọi lệnh đều chạy ở root taskflow-ai trừ khi ghi rõ khác. Với Windows, học viên có thể chạy trong Git Bash, WSL hoặc PowerShell; nếu dùng PowerShell thì giữ nguyên ý nghĩa lệnh và kiểm tra output tương đương.

Bước 1: Kiểm tra repo

Chạy ở root taskflow-ai.

git status --short

Lệnh này kiểm tra working tree. Output kỳ vọng là rỗng. Nếu có file modified/untracked, phân loại trước. Rủi ro khi bỏ qua: trộn refactor với thay đổi khác và rollback khó.

Bước 2: Tạo branch riêng

Chạy ở root taskflow-ai.

git checkout -b refactor/day-17-legacy-task-summary

Lệnh tạo branch cho bài học. Output kỳ vọng: Switched to a new branch .... Rủi ro: nếu branch tạo từ main cũ, PR có thể chứa diff lạ.

Bước 3: Thêm guardrail cho Claude Code

Thêm vào CLAUDE.md hoặc .claude/rules/refactor.md. CLAUDE.md.claude/rules/ là memory/rules giúp Claude làm đúng ý hơn, còn permission trong .claude/settings.json mới là lớp enforce bằng tool:

## Legacy refactor guardrails

- Không refactor diện rộng nếu chưa có characterization tests.
- Luôn bắt đầu bằng observe/read-only trước khi edit.
- Mỗi lần chỉ refactor một lát cắt nhỏ.
- Không đổi public API và behavior trong cùng một diff.
- Sau mỗi lát cắt phải chạy targeted test.
- Không dùng production data, secret hoặc customer data trong fixture.
- Nếu cần behavior change, tạo task riêng thay vì trộn vào refactor.

Nếu dùng .claude/settings.json, allow các command test/diff cần thiết và deny destructive commands. Ví dụ tham khảo:

{
  "permissions": {
    "allow": [
      "Bash(git status --short)",
      "Bash(git diff *)",
      "Bash(npm exec vitest -- run *)"
    ],
    "deny": [
      "Bash(git reset --hard*)",
      "Bash(git clean -fdx*)",
      "Bash(rm -rf *)",
      "Bash(npm publish*)",
      "Bash(* deploy *)",
      "Read(./.env)",
      "Read(./.env.*)",
      "Read(./secrets/**)"
    ]
  }
}

Output kỳ vọng khi mở /permissions: các rule allow/deny xuất hiện đúng scope project. Rủi ro: rule Bash dạng pattern có thể quá rộng hoặc quá hẹp; với command nguy hiểm, ưu tiên deny rõ ràng và vẫn review prompt trước khi approve. Không dùng bypassPermissions cho refactor legacy ngoài container/VM cô lập.

Bước 4: Tạo legacy module giả lập

Prompt:

Tạo module legacy giả lập cho taskflow-ai.

File:
- src/legacy/legacyTaskSummary.ts

Yêu cầu:
- Export function buildLegacyTaskSummary(tasks, nowIso).
- Cố tình trộn parsing, business rule và formatting trong một function.
- Có status TODO, IN_PROGRESS, DONE, BLOCKED.
- Có priority LOW, MEDIUM, HIGH.
- Output gồm total, done, overdue, blocked, highPriorityOpen, label.
- Không tạo test ở bước này.
- Không sửa file ngoài src/legacy/legacyTaskSummary.ts.

Bước 5: Viết characterization tests

Prompt:

Đọc src/legacy/legacyTaskSummary.ts và viết characterization tests cho behavior hiện tại.

File:
- src/legacy/legacyTaskSummary.test.ts

Ràng buộc:
- Test qua public API hiện tại.
- Cover empty input, DONE, BLOCKED, overdue, high priority open.
- Không refactor implementation.
- Nếu behavior kỳ lạ, giữ expectation hiện tại và thêm comment Characterization.

Chạy ở root taskflow-ai.

npm exec vitest -- run src/legacy/legacyTaskSummary.test.ts

Lệnh chạy riêng test legacy. Output kỳ vọng: test file pass. Rủi ro: nếu test dùng thời gian hiện tại thay vì nowIso cố định, test dễ flaky.

Bước 6: Refactor lát cắt đầu tiên

Prompt:

Refactor lát cắt nhỏ số 1.

Mục tiêu:
- Extract helper isOpenTask hoặc tương đương.
- Không đổi public API.
- Không đổi output.
- Không sửa test expectation.
- Chỉ sửa src/legacy/legacyTaskSummary.ts.
- Dừng sau khi hoàn thành và tóm tắt diff.

Chạy lại targeted test sau khi Claude sửa:

npm exec vitest -- run src/legacy/legacyTaskSummary.test.ts

Nếu fail, yêu cầu Claude phân tích expected vs actual trước khi sửa tiếp.

Bước 7: Tách formatter

Prompt:

Refactor lát cắt nhỏ số 2.

Mục tiêu:
- Extract phần tạo label thành formatSummaryLabel.
- Không đổi data shape.
- Không đổi behavior đã được tests khóa.
- Không sửa call site.

Chạy lại targeted test:

npm exec vitest -- run src/legacy/legacyTaskSummary.test.ts

Output kỳ vọng: toàn bộ test legacy vẫn pass, không có snapshot hoặc expectation bị cập nhật. Nếu Claude muốn sửa nhiều file, dừng và hỏi lý do.

Bước 8: Áp dụng Strangler Fig

Prompt:

Áp dụng Strangler Fig cho legacy task summary.

File mới:
- src/tasks/taskSummary.ts

Yêu cầu:
- Tạo implementation mới rõ type hơn.
- Giữ buildLegacyTaskSummary làm facade cho caller cũ.
- Chỉ chuyển một case đơn giản sang implementation mới.
- Không xóa legacy implementation.
- Không đổi output đã được characterization tests khóa.

Chạy ở root taskflow-ai:

npm exec vitest -- run src/legacy/legacyTaskSummary.test.ts
git diff --name-only

Output kỳ vọng: targeted test pass; git diff --name-only chỉ có src/legacy/legacyTaskSummary.ts, src/legacy/legacyTaskSummary.test.ts, src/tasks/taskSummary.ts và optional guardrail file như CLAUDE.md hoặc .claude/rules/refactor.md. Rủi ro: Strangler Fig thêm một lớp indirection tạm thời, nên phải ghi rõ case nào đã migrate và case nào vẫn đi qua legacy implementation.

Bước 9: Review diff

Prompt:

Review diff hiện tại ở chế độ read-only.

Tập trung:
- Behavior regression so với characterization tests
- Public API có bị đổi không
- Refactor có trộn behavior change không
- Diff có quá rộng không
- Fixture có chứa secret/customer data không
- Test coverage còn thiếu case nào
- Rollback có rõ không

Bước 10: Kiểm tra và rollback

Chạy ở root taskflow-ai.

git diff --name-only
git diff --check

git diff --name-only cho biết file nào thay đổi. Output kỳ vọng chỉ gồm file trong phạm vi bài thực hành: legacy module/test, implementation mới và guardrail đã chủ động thêm. git diff --check bắt whitespace/conflict marker; output kỳ vọng rỗng.

Nếu Claude sửa sai, ưu tiên /rewind trong Claude Code. Nếu cần rollback một file:

git restore src/legacy/legacyTaskSummary.ts

Lệnh này bỏ toàn bộ thay đổi chưa commit của file đó. Rủi ro: mất mọi chỉnh sửa local trong file, nên chỉ chạy sau khi đọc git diff.

Nếu Claude tạo file mới chưa được Git track, preview trước:

git clean -n src/legacy src/tasks

Output kỳ vọng: Git liệt kê đúng file untracked dự định xóa. Chỉ chạy lệnh xóa thật sau khi chắc chắn không có file của người khác. /rewind chỉ phục hồi thay đổi do Claude Code snapshot trong session, không thay thế Git và không rollback được side effect như database, deploy hoặc API call.

5. Prompt mẫu nên dùng

Khám phá:

Khám phá module legacy task summary ở chế độ read-only. Tìm public API, call sites, test hiện có và rủi ro refactor. Không chỉnh file.

Lập plan:

Lập plan refactor legacy module theo lát cắt nhỏ. Bước đầu phải là characterization tests. Ghi file dự kiến sửa, test command và rollback cho từng bước. Không implement.

Implement:

Implement đúng một lát cắt refactor đã duyệt. Không đổi public API, không đổi test expectation, không sửa file ngoài danh sách. Dừng sau lát cắt này.

Review:

Review diff hiện tại như code reviewer. Tìm regression, security risk, performance risk, maintainability risk, test gap và file ngoài phạm vi. Không sửa file.

Test/debug:

Test legacy refactor đang fail. Phân loại lỗi: test sai characterization, refactor đổi behavior, hay setup lỗi. Đề xuất fix nhỏ nhất, chưa edit.

6. Trade-offs

  • Characterization tests có thể khóa cả behavior xấu, nhưng giúp refactor an toàn.
  • Lát cắt nhỏ tạo nhiều vòng lặp hơn, nhưng diff dễ review và rollback.
  • Strangler Fig tăng complexity tạm thời, nhưng giảm blast radius khi module lớn.
  • Claude Code tăng tốc đọc và extract code, nhưng dễ over-refactor nếu prompt thiếu giới hạn.

7. Best practices

  • Luôn bắt đầu bằng git status --short.
  • Dùng /clear trước task refactor mới nếu context cũ không liên quan.
  • Dùng /compact <instructions> khi task dài, giữ behavior đã khóa, file scope và test command.
  • Dùng /rewind ngay khi Claude đi sai hướng.
  • Dùng plan mode cho observe/plan vì mode này chỉ đọc file và chạy read-only shell command; chuyển sang default hoặc accept edits khi plan đã được duyệt.
  • Ghi guardrail quan trọng vào CLAUDE.md hoặc .claude/rules/, nhưng dùng permissions.deny/hook cho ranh giới security cần enforce.
  • Không đưa production data hoặc secret vào fixture.
  • Không trộn refactor, feature và cleanup style trong cùng PR.
  • Human vẫn phải đọc diff và quyết định merge.

8. Performance / cost / context

Legacy refactor dễ tốn context vì Claude muốn đọc nhiều file. Giảm chi phí bằng cách:

  • Cho Claude đọc entrypoint, call sites và test liên quan trước, không đọc toàn repo.
  • Dùng prompt có phạm vi file rõ.
  • Chạy targeted tests sau từng lát cắt.
  • Chỉ chạy full suite trước PR hoặc khi blast radius lớn.
  • Không paste log dài; đưa failure chính và command đã chạy.

9. Checklist cuối bài

  • Có branch riêng cho Day 17.
  • Có module legacy giả lập.
  • Có characterization tests pass trước refactor.
  • Đã refactor ít nhất 2 lát cắt nhỏ.
  • Đã chạy targeted tests sau mỗi lát cắt.
  • Có facade/adapter theo Strangler Fig.
  • Có review diff read-only bằng Claude Code.
  • Có rollback plan.
  • Không có secret/customer data trong fixture.
  • Không có refactor diện rộng chưa được test khóa.

10. Bài tập

  • Bài cơ bản: tạo legacy module và characterization tests.
  • Bài thực tế: extract 2 helper nhỏ, chạy test sau mỗi lát cắt.
  • Bài nâng cao: thêm facade/adapter theo Strangler Fig cho một case.
  • Bài áp dụng cá nhân: chọn một legacy function trong project cá nhân và viết plan refactor có test, rollback, file scope.

Tài liệu

Tóm tắt kiến thức

Workflow refactor legacy an toàn:

observe -> public API -> characterization tests -> small slice -> targeted test -> review diff -> repeat

Nguyên tắc chính:

  • Test behavior hiện tại trước khi sửa cấu trúc.
  • Không rewrite toàn bộ bằng AI khi chưa có characterization tests.
  • Không trộn behavior change với refactor.
  • Dùng Strangler Fig khi module lớn hoặc có nhiều call site.
  • Dùng CLAUDE.md hoặc .claude/rules/ để ghi memory/rules, dùng .claude/settings.json hoặc hook để enforce permission.
  • Dùng /clear, /compact <instructions>/rewind để quản lý context/checkpoint, nhưng Git vẫn là nguồn rollback chính.

Sơ đồ tư duy hoặc luồng xử lý

Legacy refactor
├── Guardrails
│   ├── CLAUDE.md hoặc .claude/rules/
│   ├── permissions.deny/hooks
│   └── no broad refactor without tests
├── Characterization tests
│   ├── public API
│   ├── edge cases
│   └── no production data
├── Small slices
│   ├── extract helper
│   ├── split formatter
│   └── keep behavior stable
├── Strangler Fig
│   ├── facade
│   ├── legacy implementation
│   └── new implementation
└── Verification
    ├── targeted tests
    ├── git diff review
    └── human approval

Bảng so sánh

Cách làmƯu điểmRủi roKhi dùng
Big rewriteCode mới sạch nhanh trên giấyRegression lớn, khó reviewChỉ khi module cô lập và test rất mạnh
Small-slice refactorDễ review, dễ rollbackNhiều vòng lặp hơnMặc định cho legacy production
Characterization test trướcKhóa behavior thậtCó thể khóa cả bug cũKhi chưa hiểu đủ rule
Test sau refactorÍt việc ban đầuKhông biết lỗi do đâuTránh dùng với legacy
Strangler FigMigrate dần, giảm blast radiusTăng complexity tạm thờiModule lớn, nhiều call site
Claude Code hỗ trợNên dùngKhông nên dùng
Khám phá codeTìm call site, public APITin hoàn toàn vào kết luận mà không đọc diff
Viết testsSinh characterization casesĐể Claude sửa behavior theo ý nó
RefactorExtract helper nhỏRewrite nhiều file một lần
ReviewTìm bug/test gapThay human reviewer
GuardrailVai tròLưu ý
CLAUDE.mdMemory chung cho projectNên ngắn, chứa command/test rule thật sự cần
.claude/rules/*.mdRule theo chủ đề hoặc theo pathPhù hợp cho quy tắc refactor/testing/security
.claude/settings.jsonEnforce permissions, hooks, environmentdeny nên dùng cho secret/destructive command
Plan modeExplore/plan read-onlyKhông dùng để implement
/rewindQuay lại checkpoint trong sessionKhông thay thế Git; không rollback side effect bên ngoài

Lỗi thường gặp

  1. Refactor trước khi có characterization tests.
  2. Để Claude đổi implementation, API và tests cùng lúc.
  3. Diff quá lớn khiến reviewer không biết regression đến từ đâu.
  4. Dùng fixture từ production, gây rủi ro secret/privacy.
  5. Không cố định thời gian trong test due date.
  6. Dùng /compact không có instruction nên mất decision quan trọng.
  7. Không rollback sớm khi Claude đi sai hướng.
  8. Tin rằng prompt guardrail đủ bảo vệ secret; thực tế cần permissions.deny, hook hoặc sandbox.

Cách debug

Khi test fail sau refactor:

1. Đọc test fail.
2. So sánh expected vs actual.
3. Kiểm tra condition, null handling, timezone, sort order.
4. Nếu test characterization sai, sửa test và ghi lý do.
5. Nếu refactor đổi behavior, rollback lát cắt đó.

Chạy ở root taskflow-ai:

git diff --name-only
git diff --check
npm exec vitest -- run src/legacy/legacyTaskSummary.test.ts

Các lệnh trên lần lượt kiểm tra file đổi, lỗi diff cơ bản và targeted tests. Output kỳ vọng: danh sách file chỉ gồm legacy module/test, implementation mới và guardrail chủ động thêm; git diff --check không in gì; Vitest báo pass cho test file legacy. Rủi ro: git diff có thể hiển thị nội dung nhạy cảm nếu fixture chứa secret.

Rollback một file:

git restore src/legacy/legacyTaskSummary.ts

Chỉ dùng sau khi đã đọc diff vì lệnh này mất toàn bộ thay đổi local của file.

Nếu cần kiểm tra file mới chưa tracked:

git clean -n src/legacy src/tasks

Output kỳ vọng: chỉ liệt kê file bài tập Day 17 dự định xóa. Đây là preview, chưa xóa gì; không chạy biến thể xóa thật nếu chưa chắc chắn file không thuộc người khác.


Bài tập

Nguyên tắc chung: mọi lệnh chạy ở root taskflow-ai. Không dùng production data, secret, customer payload hoặc log thật làm fixture. Sau mỗi bài, đọc diff trước khi để Claude sửa tiếp.

Bài 1 — Cơ bản

Mục tiêu: tạo legacy module giả lập và characterization tests.

Yêu cầu:

  • Tạo src/legacy/legacyTaskSummary.ts.
  • Function chính: buildLegacyTaskSummary(tasks, nowIso).
  • TODO, IN_PROGRESS, DONE, BLOCKED, HIGH, due date quá hạn.
  • Viết src/legacy/legacyTaskSummary.test.ts.
  • Không refactor implementation trong bài này.

Prompt:

Tạo module legacy giả lập và characterization tests cho taskflow-ai.

Giới hạn:
- Chỉ sửa src/legacy/legacyTaskSummary.ts và src/legacy/legacyTaskSummary.test.ts.
- Test behavior hiện tại qua public API.
- Dùng nowIso cố định, ví dụ "2026-01-15T00:00:00.000Z".
- Không dùng dữ liệu production, secret hoặc customer payload trong fixture.
- Không sửa package/config/call site.

Chạy ở root taskflow-ai:

npm exec vitest -- run src/legacy/legacyTaskSummary.test.ts

Lệnh chạy test legacy. Output kỳ vọng: Vitest báo pass cho legacyTaskSummary.test.ts, không có test phụ thuộc thời gian hiện tại. Rủi ro: nếu test dùng new Date() trực tiếp, kết quả overdue có thể flaky.

Rollback nếu Claude tạo sai:

git restore src/legacy/legacyTaskSummary.ts src/legacy/legacyTaskSummary.test.ts

Nếu hai file là untracked, chạy git clean -n src/legacy để preview trước; chỉ xóa thật khi danh sách đúng file bài tập.

Bài 2 — Thực tế

Mục tiêu: refactor từng lát cắt nhỏ mà không đổi behavior.

Yêu cầu:

  1. Extract helper xác định task còn open.
  2. Extract helper tính overdue.
  3. Extract helper format label.
  4. Chạy targeted test sau mỗi lát cắt.
  5. Không sửa expectation test để che regression.

Prompt:

Refactor legacyTaskSummary theo một lát cắt nhỏ.

Ràng buộc:
- Extract đúng một helper.
- Không đổi public API.
- Không đổi output.
- Chỉ sửa src/legacy/legacyTaskSummary.ts.
- Dừng sau khi hoàn thành và tóm tắt diff.

Sau mỗi lát cắt, chạy:

npm exec vitest -- run src/legacy/legacyTaskSummary.test.ts
git diff --name-only

Output kỳ vọng: targeted test pass; git diff --name-only không phát sinh file ngoài src/legacy/legacyTaskSummary.ts và test đã tạo ở Bài 1. Rủi ro: nếu Claude sửa expectation test để làm pass, đó là regression bị che giấu; rollback lát cắt và yêu cầu phân tích expected vs actual.

Trade-off: nhiều lát cắt làm bài lâu hơn, nhưng reviewer nhìn được đúng ý nghĩa từng diff và rollback không kéo theo thay đổi khác.

Bài 3 — Nâng cao

Mục tiêu: dùng Strangler Fig để tạo implementation mới mà không phá caller cũ.

Yêu cầu:

  • Tạo src/tasks/taskSummary.ts.
  • Giữ buildLegacyTaskSummary làm facade hoặc adapter.
  • Chuyển một case đơn giản sang implementation mới.
  • Không xóa legacy code ngay.
  • Characterization tests vẫn pass.

Prompt:

Áp dụng Strangler Fig cho legacy task summary.

Yêu cầu:
- Tạo src/tasks/taskSummary.ts làm implementation mới.
- Giữ public API buildLegacyTaskSummary cho caller cũ.
- Chỉ route một case đơn giản sang implementation mới.
- Không đổi output đã được tests khóa.
- Nếu cần behavior change, tạo TODO và dừng.

Chạy:

npm exec vitest -- run src/legacy/legacyTaskSummary.test.ts
git diff --check

Output kỳ vọng: characterization tests vẫn pass; git diff --check không in gì. git diff --check bắt whitespace/conflict marker, nhưng không thay thế test logic.

Maintainability cần đạt: taskSummary.ts có type rõ hơn, nhưng buildLegacyTaskSummary vẫn giữ contract cũ. Performance/cost cần lưu ý: đừng yêu cầu Claude đọc toàn repo nếu chỉ cần call site và test liên quan.

Rollback nếu hướng Strangler Fig sai:

git restore src/legacy/legacyTaskSummary.ts src/tasks/taskSummary.ts

Nếu src/tasks/taskSummary.ts chưa tracked, preview bằng git clean -n src/tasks trước khi xóa.

Bài 4 — Review & Reflection

Prompt review:

Review diff Day 17 ở chế độ read-only.

Tập trung:
- Có characterization tests trước refactor chưa
- Có behavior regression không
- Có trộn behavior change với refactor không
- Có file ngoài phạm vi không
- Có security/privacy risk trong fixture không
- Có performance/context cost nào do đọc quá rộng hoặc chạy full suite quá sớm không
- Maintainability có tốt hơn hay chỉ đổi tên/đổi chỗ code
- Có rollback rõ không

Chạy:

git status --short
git diff --name-only
git diff --stat
npm test

Output kỳ vọng: git status --shortgit diff --name-only chỉ hiển thị file thực hành legacy của Day 17; git diff --stat cho blast radius nhỏ; npm test pass nếu project đã có script. Rủi ro: full suite có thể fail vì lỗi nền; cần phân loại trước khi sửa, không để Claude tự ý cleanup file ngoài phạm vi.

Reflection 10-15 dòng, trả lời:

  • Characterization test nào quan trọng nhất?
  • Claude có đề xuất refactor quá rộng không?
  • Bạn đã reject hoặc rollback gì?
  • Lát cắt nào dễ review nhất?
  • Human reviewer cần chú ý phần nào?
  • Context/cost được kiểm soát ra sao?
  • Trade-off nào chấp nhận được và trade-off nào cần task riêng?

Tiêu chí hoàn thành

  • src/legacy/legacyTaskSummary.ts.
  • src/legacy/legacyTaskSummary.test.ts.
  • Characterization tests pass trước refactor.
  • Có ít nhất 2 lát cắt refactor nhỏ.
  • Có thử facade/adapter theo Strangler Fig.
  • Không sửa expectation test để che regression.
  • Có review diff read-only bằng Claude Code.
  • Có rollback plan cụ thể.
  • Không có secret/customer data trong fixture.
  • Có ghi nhận trade-off về safety, maintainability, performance/cost/context.

Gợi ý nếu bí

  • Bắt đầu từ input/output đơn giản nhất: empty array.
  • Dùng nowIso = "2026-01-15T00:00:00.000Z" để tránh flaky test.
  • Nếu Claude muốn rewrite toàn bộ, dừng và yêu cầu chỉ implement lát cắt đầu tiên.
  • Nếu diff quá lớn, dùng /rewind rồi chia task nhỏ hơn.
  • Nếu Claude cứ đọc file không liên quan, dùng prompt nêu rõ file scope và yêu cầu plan mode/read-only.
  • Nếu fixture có dữ liệu nhạy cảm, thay bằng synthetic data trước khi viết test.

Đáp án tham khảo hoặc expected result

Expected structure:

src/
  legacy/
    legacyTaskSummary.ts
    legacyTaskSummary.test.ts
  tasks/
    taskSummary.ts

Expected behavior:

empty input -> total = 0
DONE task -> done tăng
BLOCKED task -> blocked tăng
open HIGH task -> highPriorityOpen tăng
dueDate trước nowIso và chưa DONE -> overdue tăng
label -> giữ đúng format legacy hiện tại