Chuyển đến nội dung
MINH VO Ghi chép từ công việc
của một kỹ sư tại Việt Nam
Cơ bản9 phút đọc

Bắt đầu Claude Code bằng tác vụ sửa lỗi có thể tái hiện

Chuẩn bị lỗi TypeScript có đầu vào cụ thể, chạy cùng bộ kiểm thử trước và sau bản sửa, rồi đánh giá kết quả mà không dựa vào lời khẳng định hoàn tất.

Sổ tay mở cùng những hình trừu tượng kết nối bằng sợi chỉ màu ô liu trên giấy

Hãy bắt đầu với Claude Code bằng một tác vụ mà kết quả đúng có thể được chứng minh ngoài cuộc trò chuyện. Một lỗi TypeScript nhỏ là lựa chọn phù hợp khi đã có đầu vào, kết quả sai quan sát được và lệnh kiểm thử của kho mã. Công cụ có thể hỗ trợ đọc và sửa mã; người phát triển vẫn cần tiêu chí chấp nhận rõ ràng. Bài này dành cho người đọc được hàm TypeScript và chạy được kiểm thử Node tại máy.

Tài liệu tổng quan Claude Code, được đọc ngày 21 tháng 9 năm 2026, mô tả khả năng đọc kho mã, sửa tệp và chạy lệnh phát triển. Quy trình dưới đây là một đề xuất thực hành. Nó không khẳng định Minh đã dùng công cụ trong bao lâu hoặc tiết kiệm được bao nhiêu thời gian.

Chốt hành vi trước khi yêu cầu sửa

Giả sử một API chuẩn hóa danh sách người nhận thư. Nó bỏ khoảng trắng hai đầu, bỏ mục rỗng và giữ thứ tự những địa chỉ được chấp nhận. Hàm hiện tại bỏ sót địa chỉ có chữ hoa vì so sánh trực tiếp với danh sách cho phép viết thường.

Trước khi sửa, cần quyết định quy tắc của ứng dụng: so khớp có bỏ qua hoa thường không, địa chỉ lặp có bị gộp không và lớp nào kiểm tra cú pháp địa chỉ? Ngữ nghĩa thư điện tử có nhiều chi tiết. Một quy tắc so khớp nội bộ không tự trở thành quy tắc chung cho mọi hộp thư.

Ví dụ chọn hợp đồng cụ thể: cắt khoảng trắng, tạo khóa so sánh viết thường, chỉ nhận địa chỉ trong danh sách cho phép và trả về dạng chuẩn của danh sách ấy. Mỗi địa chỉ chỉ xuất hiện một lần, theo thứ tự lần xuất hiện đầu tiên được chấp nhận.

Danh sách cho phép: ["a@example.org", "b@example.org"]
Đầu vào: [" A@example.org ", "", "b@example.org", "a@example.org"]
Kỳ vọng: ["a@example.org", "b@example.org"]

Các địa chỉ dùng miền dành cho ví dụ, không phải người nhận thật. Kiểm thử chỉ gọi hàm thuần túy, tuyệt đối không gửi thư.

Biến lỗi thành ví dụ chạy lại được

Đặt hàm cố ý sai sau vào recipients.ts trong thư mục dùng riêng cho thử nghiệm. Hàm nhận mảng chuỗi, kể cả mảng rỗng; các kiểu gốc khác hoặc phần tử không phải chuỗi đều bị từ chối. Hợp đồng yêu cầu mảng có thể duyệt lại, không nhận mọi loại iterator. Danh sách cho phép cố định trong ví dụ, nên bên gọi không thể mở rộng nó bằng đầu vào.

const allowed = new Set(["a@example.org", "b@example.org"]);

export function normalizeRecipients(value: unknown): string[] {
  if (!Array.isArray(value)) throw new TypeError("invalid recipients");
  for (const entry of value) {
    if (typeof entry !== "string") {
      throw new TypeError("invalid recipients");
    }
  }
  const result: string[] = [];
  for (const raw of value) {
    const key = raw.trim();
    if (allowed.has(key) && !result.includes(key)) result.push(key);
  }
  return result;
}

Có thể kiểm tra toàn bộ phần tử trước rồi xử lý vì đầu vào là mảng dùng lại được. Vòng lặp cũng bắt được ô trống trong mảng, vốn xuất hiện dưới dạng undefined khi duyệt. Nếu đổi tham số thành luồng chỉ đọc được một lần, phải xem lại thiết kế hai lượt này. Hàm không sửa mảng đầu vào và không tạo tác dụng gửi thư.

Lưu bộ kiểm thử độc lập thành recipients.test.mjs. Các mảng kỳ vọng được viết từ hợp đồng, không tính bằng chính hàm đang kiểm tra.

import test from "node:test";
import assert from "node:assert/strict";
import { normalizeRecipients } from "./dist/recipients.js";

const cases = [
  [[], []],
  [[" a@example.org "], ["a@example.org"]],
  [[" A@example.org ", "", "b@example.org", "a@example.org"],
   ["a@example.org", "b@example.org"]],
  [["B@example.org", "a@example.org", "b@example.org"],
   ["b@example.org", "a@example.org"]],
  [["outside@example.org", ""], []],
  [["a@example.org", "a@example.org"], ["a@example.org"]],
  [["B@example.org"], ["b@example.org"]],
  [[" b@example.org ", "a@example.org", "b@example.org"],
   ["b@example.org", "a@example.org"]]
];
for (const [index, [input, expected]] of cases.entries()) {
  test("contract " + index, () => {
    const before = structuredClone(input);
    assert.deepEqual(normalizeRecipients(input), expected);
    assert.deepEqual(input, before);
  });
}
for (const [index, input] of [null, "a@example.org", [1],
                              [undefined], new Set()].entries()) {
  test("invalid input " + index, () => {
    assert.throws(() => normalizeRecipients(input), /invalid recipients/);
  });
}

Tạo package.json tại thư mục ấy với nội dung {"type":"module"}. Khi đã có TypeScript, biên dịch bằng tsc --target ES2022 --module ES2022 --outDir dist recipients.ts, rồi chạy node --test recipients.test.mjs. Lần thực thi tại máy ngày 21 tháng 9 năm 2026 dùng TypeScript 6.0.3 và Node 22.23.2. Biên dịch thành công nhưng hàm ban đầu chỉ qua mười trường hợp và trượt ba. Thành công của trình biên dịch chưa chứng minh quy tắc so khớp đúng.

Bản sửa tham chiếu do người viết soạn chỉ đổi const key = raw.trim(); thành const key = raw.trim().toLowerCase();. Biên dịch lại rồi chạy nguyên bộ kiểm thử: cả mười ba trường hợp đều qua. Lần xuất hiện có chữ hoa nay quyết định đúng thứ tự đầu ra, kể cả khi phía sau có bản viết thường trùng địa chỉ.

Đây là so khớp với danh sách cho phép của ứng dụng. Nó không khẳng định phần đứng trước dấu @ của mọi địa chỉ đều không phân biệt hoa thường. Chuẩn hóa hộp thư Unicode, xác nhận cú pháp, khả năng nhận thư và gửi thư thật nằm ngoài ví dụ.

Ba trường hợp có chữ hoa thất bại ở hàm ban đầu dù biên dịch thành công; bản sửa thủ công vượt qua nguyên bộ mười ba trường hợpMở ảnh kích thước đầy đủ ↗

Các tệp thử nghiệm được viết thủ công và đã chạy thực tế. Lệnh claude --version tại máy báo 2.1.278, nhưng không có phiên mô hình Claude nào sinh bản sửa hoặc tham gia thử nghiệm tính giờ. Hãy dùng ví dụ làm hồ sơ tác vụ cho phiên của bạn rồi ghi lại điều thật sự xảy ra. Trong bài viết ngày 11 tháng 3 năm 2025 về dùng LLM để lập trình, Simon Willison nhấn mạnh trách nhiệm của lập trình viên phải nhìn thấy phần mềm chạy. Bản lỗi và bộ kiểm thử không đổi biến trách nhiệm ấy thành bằng chứng có thể tái hiện.

Cung cấp hồ sơ tác vụ có giới hạn

Gửi kiểm thử thất bại hoặc lệnh tái hiện, vị trí hàm nếu đã biết và hợp đồng hành vi. Yêu cầu đọc các nơi gọi trước khi sửa vì một hàm dùng chung có thể ảnh hưởng nhiều endpoint. Kèm hướng dẫn hiện có của kho mã về lệnh kiểm tra và quy ước.

Tài liệu thực hành Claude Code khuyên cung cấp cách xác minh kết quả. Hồ sơ tác vụ vì vậy cần cả đích đến và phép kiểm tra. Yêu cầu “cải thiện hàm này” còn bỏ ngỏ quá nhiều quyết định về tính đúng, phong cách và phạm vi.

Hợp đồng chuẩn hóa dẫn tới bản sửa và bước chấp nhận dựa trên các kết quả cụ thểMở ảnh kích thước đầy đủ ↗

Hình tách hợp đồng đầu vào khỏi phép kiểm tra chấp nhận. Công cụ có thể dùng tập hợp, ánh xạ hoặc vòng lặp. Trước khi tranh luận về cấu trúc triển khai, hãy đối chiếu kết quả cùng các trường hợp biên.

Nếu kho mã đang có thay đổi chưa commit, hãy kiểm kê trước và giữ nguyên công việc không thuộc tác vụ. Nhánh hoặc worktree riêng có thể giúp nhiều tác vụ cùng diễn ra, nhưng vẫn cần đọc diff tổng hợp ở cuối. Cô lập thư mục không tự chứng minh bản sửa đúng.

Theo dõi quá trình tìm nguyên nhân

Những bước đầu hữu ích thường là đọc kiểm thử, hàm và nơi gọi. Nếu công cụ bắt đầu thay cả thư viện kiểm tra dữ liệu, cần xem lỗi đang sửa có thực sự đòi phạm vi ấy không. Lỗi nhỏ thường chỉ cần thay đổi nhỏ, nhưng mã hiện có cũng có thể bộc lộ vấn đề hợp đồng rộng hơn.

Tách lỗi môi trường khỏi lỗi mã. Cài dependency chưa xong không chứng minh bản sửa làm hỏng kiểm thử; cài thành công cũng chưa nói gì về hành vi đã sửa. Gắn mỗi kết quả lệnh với đúng câu hỏi mà nó trả lời.

Cho phép thay đổi giả thuyết khi mã mâu thuẫn với mô tả ban đầu. Hồ sơ có thể chỉ đúng triệu chứng nhưng đoán sai nơi gây lỗi. Điều cần giữ là bản sửa giải thích được hành vi quan sát, và việc mở rộng phạm vi phải có lý do cụ thể.

Xem tính độc lập của kiểm thử và diff cuối

Bộ thử chuẩn hóa cần bắt được chữ hoa, khoảng trắng, địa chỉ lặp và địa chỉ ngoài danh sách. Không dùng hàm mới để tính kết quả kỳ vọng, vì như vậy cả triển khai lẫn phép kiểm tra có thể cùng sai.

Kiểm thử hồi quy hữu ích phải thất bại trên hàm cũ và qua trên hàm đã sửa. Dùng bản ghi lần chạy trước khi sửa hoặc checkout riêng để xác nhận quan hệ đó; không xóa công việc hiện tại chỉ để tái tạo lỗi cũ. Nếu đổi kỳ vọng để khớp kết quả sai, ta đã đổi yêu cầu thay vì sửa lỗi.

Sau khi kiểm thử qua, đọc toàn bộ bản thay đổi: kiểu trả về có ổn định không, đầu vào sai được xử lý theo hợp đồng chưa, định dạng không liên quan có che mất thay đổi hành vi không? Nếu hàm phục vụ một endpoint, kiểm tra thêm ở mức endpoint để xác nhận địa chỉ chuẩn đi đến đúng phản hồi hoặc bản nháp xử lý tiếp. Bộ thử thuần túy trên đây chưa bao phủ tuyến ấy.

Bàn giao cần nêu thay đổi, lệnh thật sự đã chạy, kết quả hoàn tất và phần chưa xác minh. “Tất cả kiểm thử đều qua” cần có bằng chứng tiến trình kết thúc thành công, nhất là khi lệnh chậm hoặc từng bị ngắt. Tác vụ đầu tiên hoàn tất khi người khác có thể giải thích bản sửa từ các đầu vào cụ thể và tự tái hiện bằng chứng.

Nguồn tham khảo & đọc thêm

  1. Claude Code overview
  2. Claude Code best practices
  3. Simon Willison: How I use LLMs to help me write code, 11 March 2025
← Về trang bài viết

Hình minh họa

100%