Tool tích hợp sẵn
Những điều bạn cần biết
Claude Code cung cấp sáu tool tích hợp sẵn để làm việc với kho mã: Read, Write, Edit, Bash, Grep, và Glob. Mỗi tool có một mục đích cụ thể, và dùng sai tool cho một tác vụ sẽ lãng phí thời gian, token context, hoặc cả hai. Đề thi cố tình đưa ra các tình huống mà việc nhầm lẫn giữa các tool này dẫn đến câu trả lời sai.
Grep so với Glob: Sự phân biệt cốt lõi
Đây là sự phân biệt quan trọng nhất trong toàn bộ task statement này. Nhầm chỗ này là bạn sẽ mất điểm.
Grep tìm kiếm mẫu trong NỘI DUNG file. Dùng Grep khi bạn cần tìm văn bản bên trong file. Nơi gọi hàm. Thông báo lỗi. Câu lệnh import. Phép gán biến. Bất cứ khi nào bạn tìm kiếm thứ mà các file CHỨA, Grep là tool phù hợp.
// Tìm tất cả file gọi processLegacyOrder() Grep: "processLegacyOrder" // Tìm tất cả thông báo lỗi chứa "timeout" Grep: "timeout" // Tìm tất cả file import một module cụ thể Grep: "import.*from 'utils/auth'"
Glob khớp ĐƯỜNG DẪN file theo mẫu đặt tên. Dùng Glob khi bạn cần tìm file theo tên, phần mở rộng, hoặc cấu trúc thư mục. File test. File cấu hình. Tất cả file TypeScript trong một thư mục cụ thể. Bất cứ khi nào bạn tìm kiếm file dựa trên đường dẫn của chúng, Glob là tool phù hợp.
// Tìm tất cả file test Glob: "**/*.test.tsx" // Tìm tất cả file cấu hình Glob: "**/config.*" // Tìm tất cả file MDX trong thư mục domains Glob: "content/domains/**/*.mdx"
Sự phân biệt trong một câu: Grep tìm những gì ở BÊN TRONG file. Glob tìm file theo TÊN của chúng.
Đề thi đưa ra các tình huống mà một lập trình viên dùng sai tool. Nếu ai đó dùng Glob để tìm nơi gọi hàm, nó sẽ thất bại — Glob khớp đường dẫn, không phải nội dung. Nếu ai đó dùng Grep để tìm file test theo mẫu đặt tên, về mặt kỹ thuật nó có thể hoạt động (bằng cách tìm kiếm "test" trong tên file qua nội dung), nhưng đó là sai tool và đề thi kỳ vọng bạn xác định đúng tool cần dùng.
Read, Write, và Edit
Ba tool này xử lý các thao tác trên file, mỗi tool được tối ưu cho một use case khác nhau.
Edit thực hiện chỉnh sửa có mục tiêu bằng cách khớp văn bản duy nhất. Bạn chỉ định chính xác văn bản cần tìm và văn bản thay thế. Nó nhanh và chính xác vì chỉ chạm vào đúng đoạn văn bản bạn chỉ định.
Edit: old_string: "function processOrder(id: string)" new_string: "function processOrder(id: string, validate: boolean = true)"
Khi nào Edit thất bại: Edit đòi hỏi khớp văn bản duy nhất. Nếu văn bản bạn chỉ định xuất hiện ở nhiều nơi trong file, Edit không thể xác định nên thay đổi vị trí nào và sẽ thất bại. Đây không phải là lỗi (bug) — đó là một cơ chế an toàn nhằm ngăn chặn các thay đổi ngoài ý muốn.
Khi Edit không tìm được điểm neo duy nhất. Theo tài liệu về tool Edit, cách khắc phục là mở rộng old_string với thêm ngữ cảnh xung quanh cho đến khi nó xác định được đúng một vị trí, hoặc đặt replace_all: true nếu bạn thực sự muốn cập nhật mọi lần xuất hiện. Cả hai cách đều giữ bạn ở lại với Edit và tốn gần như không thêm context nào. Read + Write (tải toàn bộ file, ghi lại toàn bộ file) là phương án cuối cùng. Nó vẫn hoạt động. Nhưng bạn đã tốn cả một file token cho một thay đổi thường chỉ là một dòng.
Thứ tự ưu tiên:
- Thử Edit với điểm neo ngắn nhất có khả năng là duy nhất.
- Khi gặp khớp không duy nhất, mở rộng
old_stringcho đến khi nó khớp đúng một vị trí, hoặc dùngreplace_all: truenếu bạn muốn thay đổi mọi lần xuất hiện. - Chỉ chuyển sang Read + Write khi cả hai cách trên đều không thể xác định rõ mục tiêu.
Đừng mặc định dùng Read + Write cho mọi thay đổi. Đề thi trừ điểm điều đó vì nó tiêu tốn token context. Nó cũng trừ điểm việc nhảy thẳng từ một lần Edit thất bại do khớp không duy nhất sang Read + Write. Mở rộng điểm neo hoặc dùng replace_all là phản hồi đã được ghi nhận chính thức. Read + Write là phương án dự phòng, không phải bước tiếp theo.
Hiểu kho mã theo cách tăng dần
Cách bạn khám phá một kho mã quan trọng không kém việc bạn dùng tool nào. Có một cách đúng và một cách sai.
Sai: Đọc tất cả file ngay từ đầu. Tải mọi file vào context trước khi bạn hiểu mình cần gì là một kẻ hủy diệt ngân sách context. Nếu một kho mã có 200 file và bạn đọc hết tất cả, bạn đã tiêu tốn toàn bộ context window vào những file phần lớn không liên quan đến tác vụ của bạn. Đây là anti-pattern lớn nhất trong việc khám phá kho mã.
Đúng: Khám phá theo cách tăng dần. Bắt đầu hẹp. Chỉ mở rộng khi cần thiết.
-
Grep để tìm điểm bắt đầu. Tìm kiếm tên hàm, tên class, hoặc thông báo lỗi làm điểm neo cho cuộc điều tra của bạn. Điều này cho bạn biết file nào liên quan.
-
Read để theo dõi import và truy vết luồng xử lý. Sau khi biết file nào quan trọng, hãy Read chúng để hiểu cấu trúc mã. Theo dõi các câu lệnh import để phát hiện file liên quan.
-
Grep lại để truy vết cách sử dụng. Nếu bạn tìm thấy một hàm wrapper hoặc re-export, hãy Grep tên đó trên toàn bộ kho mã để tìm mọi nơi sử dụng.
-
Chỉ Read những gì bạn cần. Mỗi file bạn đọc nên được lý giải bởi những gì bạn phát hiện ở bước trước.
Cách tiếp cận này dùng context tối thiểu để đạt được sự hiểu biết tối đa. Bạn khám phá cấu trúc kho mã một cách dần dần, chỉ tốn token vào những file liên quan đến tác vụ của bạn.
Truy vết cách sử dụng hàm qua các module wrapper
Một mẫu hình phổ biến trong kho mã: một hàm được định nghĩa trong một module, được re-export qua một wrapper, và được sử dụng thông qua tên của wrapper đó. Grep đơn giản cho tên hàm gốc sẽ bỏ lỡ những nơi sử dụng import qua wrapper.
Cách tiếp cận đúng:
- Grep cho định nghĩa hàm để tìm nơi nó được định nghĩa
- Read file định nghĩa để xác định các tên được export
- Grep cho từng tên được export trên toàn bộ kho mã để tìm mọi nơi sử dụng
- Nếu hàm được re-export qua một barrel file (ví dụ
index.ts), Grep tên module của barrel file để tìm những nơi import từ đó
Việc truy vết nhiều bước này kỹ lưỡng hơn một lần Grep đơn lẻ và bắt được cả những nơi sử dụng gián tiếp mà một tìm kiếm đơn giản có thể bỏ lỡ.
Tình huống loại bỏ (deprecation)
Tình huống này xuất hiện thường xuyên trong quá trình ôn thi: tìm tất cả file gọi một hàm đã bị loại bỏ VÀ tìm các file test kiểm tra hàm đó. Trình tự tool đúng:
- Grep tên hàm — tìm mọi file có nội dung tham chiếu đến hàm đó, bao gồm cả các test import trực tiếp (tìm kiếm nội dung)
- Glob cho các file test liền kề — tìm file test tương ứng với mỗi nơi gọi theo quy ước đặt tên, ví dụ
OrderProcessor.ts→OrderProcessor.test.tsx, ngay cả khi test kiểm tra hàm đó một cách gián tiếp qua module nguồn (khớp đường dẫn) - Grep lại cho tên wrapper — khi một nơi gọi lộ hàm đó thông qua một wrapper (ví dụ
applyLegacyOrdergọiprocessLegacyOrderbên trong), hãy Grep tên wrapper để tìm các test bao phủ hàm đó một cách gián tiếp qua wrapper
Ví dụ, nếu Grep cho thấy OrderProcessor.ts và RefundHandler.ts gọi hàm đã bị loại bỏ, hãy Glob cho **/OrderProcessor.test.* và **/RefundHandler.test.* để lấy về các file test liền kề của chúng, ngay cả khi các test đó không hề nhắc đến processLegacyOrder theo tên. Nếu một trong các file nguồn bọc hàm đó dưới một tên mới, hãy Grep tên wrapper đó để bắt thêm các test khác.
Đây là Grep, rồi Glob, rồi Grep lại — tìm kiếm nội dung cho tham chiếu trực tiếp, khớp đường dẫn cho các test liền kề, tìm kiếm nội dung cho phạm vi bao phủ gián tiếp. Không phải Glob trước.
Khái niệm chính
Grep tìm kiếm nội dung file. Glob khớp đường dẫn file. Edit là lựa chọn mặc định cho các thay đổi. Khi gặp khớp không duy nhất, mở rộng điểm neo hoặc dùng replace_all: true. Read + Write là phương án dự phòng cuối cùng. Hãy xây dựng hiểu biết về kho mã theo cách tăng dần. Đừng bao giờ đọc tất cả file ngay từ đầu.
Bẫy thi
Bẫy thi: Dùng Glob để tìm nơi gọi hàm (nó tìm đường dẫn, không phải nội dung)
Glob khớp đường dẫn file theo mẫu đặt tên. Nó không thể tìm kiếm bên trong file để tìm lệnh gọi hàm. Dùng Grep để tìm kiếm nội dung file cho tên hàm, câu lệnh import, hoặc thông báo lỗi.
Bẫy thi: Dùng Grep để tìm file theo phần mở rộng hoặc mẫu đặt tên
Dù về mặt kỹ thuật Grep có thể tìm thấy tên file được nhắc đến trong nội dung, Glob là tool được thiết kế chuyên biệt để khớp đường dẫn file. Dùng Glob cho **/*.test.tsx, **/config.*, và các tìm kiếm dựa trên đường dẫn tương tự.
Bẫy thi: Đọc tất cả file nguồn ngay từ đầu trước khi hiểu file nào liên quan
Tải mọi file vào context là một kẻ hủy diệt ngân sách context. Cách tiếp cận đúng là tăng dần: Grep để tìm điểm bắt đầu, rồi Read để truy vết luồng xử lý từ những điểm bắt đầu cụ thể đó.
Bẫy thi: Mặc định dùng Read + Write cho mọi thay đổi file thay vì thử Edit trước
Edit nhanh hơn và dùng ít context hơn vì nó chỉ chạm vào đúng đoạn văn bản cần thiết. Read + Write tải toàn bộ file. Hãy thử Edit trước; mở rộng điểm neo hoặc dùng replace_all khi Edit báo khớp không duy nhất. Read + Write là phương án dự phòng cuối cùng, không phải phản hồi tiêu chuẩn.
Bẫy thi: Nhảy thẳng sang Read + Write ngay khi Edit báo khớp không duy nhất
Cách khắc phục Edit đã được ghi nhận chính thức là mở rộng old_string với ngữ cảnh xung quanh cho đến khi nó xác định được đúng một lần xuất hiện, hoặc đặt replace_all: true cho một thay đổi toàn cục. Cả hai đều giữ bạn ở lại với Edit và tốn gần như không đáng kể. Chỉ leo thang sang Read + Write khi không cách nào trong hai cách trên có thể xác định rõ mục tiêu.
Tình huống luyện tập
Không tìm thấy câu hỏi luyện tập "q-2-5-001".
Bài tập thực hành
Truy vết và tái cấu trúc một hàm đã bị loại bỏ bằng các tool tích hợp sẵn
Bạn sẽ học được gì
- Áp dụng Grep cho tìm kiếm nội dung và Glob cho khớp đường dẫn theo đúng trình tự
- Dùng khám phá kho mã theo cách tăng dần thay vì đọc tất cả file ngay từ đầu
- Chọn Edit làm tool chỉnh sửa chính và mở rộng điểm neo (hoặc dùng replace_all) khi Edit báo khớp không duy nhất
- Truy vết cách sử dụng hàm qua các module wrapper và barrel file
- Tuân theo mẫu hình Grep-rồi-Glob để tìm nơi gọi hàm và file test của chúng
Các bước
Vì sao: Grep tìm kiếm nội dung file — đó là tool đúng để tìm nơi gọi hàm. Dùng Glob ở đây sẽ thất bại vì Glob khớp đường dẫn file, không phải nội dung. Đề thi kiểm tra trực tiếp sự phân biệt này và trừ điểm những thí sinh nhầm lẫn giữa hai tool.
Bạn sẽ thấy: Một danh sách đường dẫn file chứa lệnh gọi processLegacyOrder, kèm số dòng và dòng khớp cho thấy chính xác vị trí gọi. Ví dụ: src/OrderProcessor.ts:42: await processLegacyOrder(orderId).
Vì sao: Glob khớp đường dẫn file theo mẫu đặt tên — đó là tool đúng để tìm file test theo phần mở rộng hoặc quy ước đặt tên. Điều này hoàn thiện mẫu hình Grep-rồi-Glob: tìm kiếm nội dung để tìm nơi gọi hàm, rồi khớp đường dẫn để tìm test của chúng.
Bạn sẽ thấy: Một danh sách đường dẫn file test khớp với mẫu, chẳng hạn src/OrderProcessor.test.tsx và src/RefundHandler.test.tsx. Chúng tương ứng với các file gọi hàm được tìm thấy bởi Grep ở bước trước.
Vì sao: Đọc file theo cách tăng dần — chỉ sau khi Grep xác định file nào quan trọng — là cách tiếp cận đúng. Đọc tất cả file nguồn ngay từ đầu là một kẻ hủy diệt ngân sách context mà đề thi trừ điểm rõ ràng. Mỗi lần Read nên được lý giải bởi những gì bạn phát hiện ở bước trước.
Bạn sẽ thấy: Toàn bộ nội dung của từng file gọi hàm, cho thấy processLegacyOrder được gọi như thế nào, tham số nào được truyền vào, giá trị trả về được dùng ra sao, và liệu hàm có được import trực tiếp hay qua một module wrapper.
Vì sao: Edit là tool chỉnh sửa được ưu tiên vì nó nhắm vào đúng đoạn văn bản cụ thể và dùng ít context hơn Read + Write. Đề thi trừ điểm việc mặc định dùng Read + Write cho mọi thay đổi. Luôn thử Edit trước — nó nhanh hơn và chính xác hơn.
Bạn sẽ thấy: Từng file gọi hàm được cập nhật với lệnh gọi API mới thay thế lệnh gọi cũ đã bị loại bỏ. Ví dụ, processLegacyOrder(orderId) được thay bằng processOrder(orderId, { validate: true }). Tool Edit xác nhận việc thay thế đã thành công.
Vì sao: Edit thất bại khi văn bản mục tiêu xuất hiện nhiều lần trong file — đây là một cơ chế an toàn, không phải lỗi. Theo tài liệu về tool Edit, cách khắc phục đã được ghi nhận chính thức là mở rộng điểm neo với thêm ngữ cảnh xung quanh cho đến khi nó khớp đúng một vị trí, hoặc dùng replace_all cho các thay đổi toàn cục. Cả hai cách đều giữ bạn ở lại với Edit và tốn gần như không đáng kể về context. Read + Write tải toàn bộ file cho một thay đổi thường chỉ là một dòng duy nhất — hãy giữ nó như phương án cuối cùng.
Bạn sẽ thấy: Ở lần thử đầu tiên, Edit thất bại với lỗi kiểu như: old_string matches 3 locations. Khi thử lại với old_string được mở rộng bao gồm tên hàm xung quanh hoặc dòng liền kề duy nhất, Edit thành công và thay đổi đúng một lần xuất hiện. Nếu replace_all: true là lựa chọn đúng, mọi lần xuất hiện được cập nhật đồng thời.