Quay lại blog
14 tháng 4, 2026Sergei Solod6 phút đọc

Vì sao TypeScript hoạt động rất tốt với Codex khi xây phần mềm production

Trong môi trường production, TypeScript mang lại cho Codex thứ mà một prompt hay không thể thay thế: contract kiểm tra được bằng máy, feedback nhanh từ compiler và con đường an toàn hơn cho các refactor lớn.

TypeScriptCodexLập trình với AIGiao phần mềmJavaScriptQuy trình phát triển

Trước đây tôi thường diễn đạt quan điểm này theo cách cố tình gây tranh luận: TypeScript là ngôn ngữ tốt nhất cho Codex. Bây giờ tôi sẽ nói chính xác hơn. Với kiểu công việc frontend và full-stack production mà tôi làm, TypeScript là môi trường nơi Codex dễ kiểm soát, kiểm chứng và sử dụng đáng tin cậy nhất.

Từ quan trọng không phải là “TypeScript” mà là khả năng kiểm soát. AI coding agent rất giỏi tạo ra code trông hợp lý. Nhưng production engineering đòi hỏi nhiều hơn: code phải khớp contract hiện có, giữ nguyên behavior, sống sót qua refactor và báo lỗi sớm khi giả định sai. TypeScript biến nhiều ràng buộc như vậy thành thông tin máy có thể đọc và kiểm tra, nhiều hơn đáng kể so với JavaScript thuần.

Codex thực sự cần gì từ một codebase

Prompt tốt có ích, nhưng nó chỉ là một nguồn context. Bản thân codebase có thể giúp nhiệm vụ dễ hiểu hơn hoặc buộc model phải đoán. Trong thực tế, môi trường tốt nhất là nơi các giả định quan trọng được thể hiện rõ và tự động kiểm tra.

  • Function signature xác định input và output hợp lệ.
  • Interface và type làm rõ shape của domain object.
  • Union và enum thu hẹp tập state hợp lệ.
  • Compiler biến nhiều giả định sai thành error cụ thể ngay sau khi edit.

AI agent không cần hiểu hoàn hảo toàn bộ ứng dụng nếu ứng dụng có thể phản hồi ngay khi agent phá contract. Feedback loop càng ngắn thì càng ít chỗ cho những phỏng đoán quá tự tin.

Type là context có thể kiểm chứng, không chỉ là tài liệu

TypeScript tốt giúp con người hiểu intent. Trong workflow tự động, nó còn có giá trị hơn vì phần “tài liệu” đó có thể được máy kiểm tra. Ví dụ:

type PaymentResult =
  | { ok: true; receiptId: string }
  | { ok: false; code: "declined" | "timeout" };

function getReceiptId(result: PaymentResult) {
  if (result.ok) return result.receiptId;

  return result.receiptId;
  // TypeScript: Property 'receiptId' does not exist
  // on the failure branch.
}

Trong JavaScript, cùng một lỗi có thể trông hợp lý cho đến khi branch đó thực sự chạy ở runtime. TypeScript có thể từ chối giả định sai trước khi merge. Với Codex, compiler error không chỉ là gạch đỏ trong editor mà là tín hiệu khá chính xác về điều model đã hiểu sai.

Vì vậy tôi thích domain type cụ thể hơn các cấu trúc rộng như Record<string, any>. Type càng mô tả đúng thực tế, codebase càng trở thành context hữu ích cho cả developer và AI.

Compiler biến sai lầm thành feedback loop

Tôi không tin vào quy trình “yêu cầu Codex viết code rồi chấp nhận câu trả lời đầu tiên”. Workflow đáng tin cậy phải lặp:

task → inspect → edit → typecheck → lint → test → review diff

Chuỗi này nhàm chán, và chính vì thế nó hiệu quả. Sau mỗi thay đổi có ý nghĩa, môi trường phản hồi. Type error tìm contract bị phá. Lint tìm nhóm vấn đề khác. Test kiểm tra behavior mà type không biểu diễn được. Diff cuối vẫn cần human review.

Prompt thông minh có thể cải thiện lần thử đầu. Verification loop mạnh cải thiện mọi lần thử. Với production, điều đó quan trọng hơn nhiều.

Refactor lớn là nơi TypeScript đặc biệt có giá trị

Edit nhỏ, cô lập thì hầu như coding assistant nào cũng xử lý được. Khó hơn là thay đổi lan qua hàng chục file: đổi tên field domain, thay API response, siết component props, tách union hoặc thay abstraction cũ.

Với TypeScript, breaking change tạo ra bản đồ các giả định bị ảnh hưởng. Compiler chỉ ra nơi contract cũ vẫn đang được dùng. Codex có thể xử lý danh sách đó rồi kiểm tra lại sau mỗi vòng. Điều này không tự động khiến refactor đúng, nhưng làm blast radius trở nên nhìn thấy được.

Trong codebase ít type, dependency bị bỏ sót đôi khi chỉ xuất hiện dưới dạng bug ở một màn hình mà chẳng ai mở trong lúc phát triển.

Những gì TypeScript không cứu được

TypeScript là guardrail, không phải bằng chứng về tính đúng đắn:

  • Business logic sai: code typed hoàn hảo vẫn có thể tính sai.
  • Dữ liệu runtime: API, database, form hay third-party service có thể trả dữ liệu khác giả định compile-time.
  • Thiếu product context: compiler không biết vì sao flow tồn tại hoặc edge case nào quan trọng với user.
  • Security và architecture: type hợp lệ không đảm bảo authorization, caching, database boundary hay infrastructure đúng.

Vì thế runtime validation, test, logging, observability và review vẫn bắt buộc. Typecheck sạch không đồng nghĩa feature đúng.

Workflow tôi dùng cho thay đổi có AI hỗ trợ

  1. Đặt mục tiêu hẹp. Cái gì thay đổi, cái gì không và acceptance criteria là gì.
  2. Để Codex inspect trước khi edit. Type hiện có, call site, test và module lân cận thường hữu ích hơn prompt dài.
  3. Ưu tiên thay đổi nhỏ nhất nhưng nhất quán. Diff tập trung dễ kiểm chứng hơn một rewrite “tiện thể” không liên quan.
  4. Chạy typecheck và lint ngay. Tôi muốn structural error xuất hiện khi thay đổi còn nhỏ.
  5. Chạy test liên quan và thêm test nếu behavior đổi. Type kiểm contract; test kiểm behavior.
  6. Tự review diff cuối. Naming, architecture, duplication, edge case và việc implementation có thực sự giải quyết vấn đề ban đầu hay không.

Khi JavaScript vẫn hoàn toàn đủ

Đây không phải lập luận rằng JavaScript tệ. Với script nhỏ, prototype dùng một lần, automation đơn giản hoặc code có ít shared state, TypeScript có thể không mang lại nhiều lợi ích. Project JavaScript có test tốt và convention rõ cũng có thể là môi trường AI rất mạnh.

Quan điểm của tôi hẹp hơn: codebase càng lớn, tồn tại lâu và có nhiều người cùng sửa, constraint kiểm tra được bằng máy càng có giá trị. Và cũng chính lúc đó lỗi do AI tạo ra trở nên đắt hơn.

Lý do thật sự tôi thích TypeScript khi dùng Codex

TypeScript không làm Codex thông minh hơn. Nó làm môi trường ít dung thứ hơn với các giả định tệ.

Trong production tôi không chỉ muốn một agent sáng tạo; tôi muốn agent hoạt động trong một hệ thống liên tục nói cho nó biết khi nào nó sai. Type, compiler error, lint, test và human review cùng tạo nên hệ thống đó.

Vì vậy khi tôi nói TypeScript làm việc rất tốt với Codex, đó không phải sự cuồng ngôn ngữ. Đó là lựa chọn feedback nhanh và có thể kiểm tra bằng máy: ít mơ hồ hơn, blast radius nhỏ hơn, refactor an toàn hơn và—khi verification được thực hiện nghiêm túc—tốc độ engineering đáng tin cậy hơn nhiều.