Kidslen là bốn ứng dụng: một backend Kotlin/Spring Boot, một dashboard phụ huynh React 19, một ops console React 19, và một companion app React Native. Cộng thêm marketing site. Một người, làm buổi tối và cuối tuần.

Chúng được xây song song bởi nhiều AI agent, trong vài ngày thay vì vài tháng. Đó là câu mà mọi người muốn nghe, nên để tôi nói trước sự thật nhàm chán: agent không phải phần khó. Bản hợp đồng mới khó.

Nút thắt chưa bao giờ là gõ phím

Nếu bạn đã dùng agent một cách nghiêm túc, bạn biết chế độ hỏng này. Một agent xây endpoint đăng nhập trả về { accessToken, refreshToken }. Một agent khác xây màn hình đăng nhập chờ { token, refresh }. Cả hai đều đúng. Cả hai đều có test xanh. Không cái nào chạy được với cái kia, và bạn phát hiện ra lúc tích hợp — thời điểm đắt đỏ nhất có thể.

Chạy bốn agent song song nhân vấn đề đó lên hơn bốn lần, vì mỗi cặp thành phần đều có một interface để bất đồng. Nhanh ở từng thành phần chẳng ích gì nếu các thành phần không gặp nhau.

Nên trước khi agent nào viết code, tôi viết bản hợp đồng API.

Tài liệu hợp đồng

Một tài liệu. Nó là thứ giá trị nhất của dự án, và nó tồn tại trước dòng implementation đầu tiên. Nó đặc tả:

  • Mọi endpoint: method, path, yêu cầu xác thực, schema request, schema response, status code.
  • Định dạng lỗi: RFC 7807 problem details, có liệt kê đầy đủ các type URI. Không phải “trả về lỗi” — mà là hình dạng chính xác, cho từng trường hợp lỗi.
  • Ngữ nghĩa auth: refresh token xoay vòng, phát hiện tái sử dụng làm gì, chuyện gì xảy ra khi refresh bằng token đã xoay, thời hạn token.
  • Phân trang: tham số chính xác, phong bì chính xác, page size tối đa bắt buộc.
  • Schema ingest sự kiện: hình dạng lô upload, idempotency key, server bảo đảm gì về bản trùng.
  • Luồng SSE: tên event, hình dạng payload, ngữ nghĩa kết nối lại và replay.
  • Mô hình policy: policy là gì, bốn loại, một vi phạm được biểu diễn ra sao.
  • Quy ước đặt tên và ngày giờ: camelCase, timestamp UTC ISO-8601, ID là chuỗi opaque. Nhàm chán, và nó xoá sổ nguyên một lớp bất đồng.

Rồi tôi đưa cho mỗi agent đúng lát cắt liên quan của tài liệu đó, kèm một chỉ thị cứng: hợp đồng là nguồn sự thật; nếu có gì mơ hồ, dừng lại và hỏi, đừng tự quyết.

Mệnh đề cuối đó làm được nhiều việc hơn bất kỳ mẹo prompt nào. Hành vi mặc định của agent khi spec không rõ là chọn một phương án hợp lý rồi tự tin đi tiếp, và hai agent cùng chọn phương án hợp lý một cách độc lập chính là cách bạn có accessToken đối đầu token.

Vì sao song song thật sự chạy được

Với bản hợp đồng đã có, bốn luồng công việc trở nên độc lập thật sự:

  • Agent backend implement endpoint khớp hợp đồng, với test khẳng định đúng các hình dạng trong hợp đồng.
  • Agent dashboard phụ huynh xây dựa trên response của hợp đồng, được mock, trước khi backend tồn tại.
  • Agent ops console làm tương tự cho bề mặt quản trị.
  • Agent companion app xây luồng enroll, màn hình minh bạch, khung capture và hàng đợi upload dựa trên hợp đồng ingest.

Ngày tích hợp không phải thảm hoạ như thường lệ. Không phải vì agent xuất sắc, mà vì interface đã được một con người chốt trước, và các bất đồng về nó là bug của hợp đồng — sửa ở một chỗ, cả bốn phía cập nhật theo cùng một sửa đổi.

Điều tôi muốn nói với bất kỳ ai định thử: tài liệu hợp đồng chính là tính song song. Agent chỉ là throughput. Bỏ qua tài liệu thì bạn chưa song song hoá gì cả; bạn chỉ tạo ra bốn codebase không tương thích một cách rất nhanh.

Agent làm tốt cái gì

Công bằng mà nói, chúng thật sự giỏi ở:

  • Bề rộng của phần boilerplate. Controller, DTO, mapper, migration, validate form, component bảng, trạng thái loading và rỗng. Cái khối lượng không hào nhoáng làm một app trông như đã hoàn thiện, được làm nhất quán trên cả bốn codebase.
  • Tuân thủ một quy ước tường minh khi nó đã được viết ra. Mọi endpoint trả lỗi RFC 7807, mọi danh sách phân trang cùng kiểu, mọi timestamp là UTC. Tính nhất quán là chỗ agent vượt trội hơn một con người mệt mỏi lúc nửa đêm.
  • Bộ khung test. Cho trước một hành vi đã đặc tả, sinh ra các ca kiểm thử quanh nó — kể cả các ca biên tôi chưa liệt kê, đặc biệt quanh ranh giới phân trang và tập rỗng.
  • Dịch xuyên ngôn ngữ. “Đây là type TypeScript trong hợp đồng; tạo data class Kotlin” là việc máy móc, dễ sai với người và gần như miễn phí với agent.
  • Tốc độ lặp UI. Sắp xếp lại layout dashboard chỉ mất vài phút, nên tôi thử được ba phương án thay vì phải bảo vệ phương án đầu tiên.

Những gì tôi phải tự kiểm chứng

Đây là phần quan trọng, và là phần tôi muốn ghi vào biên bản.

Tôi tự chạy lại mọi bộ test. Tôi không tin một báo cáo nào nói rằng test đã xanh.

Đây không phải hoang tưởng rằng ai đó nói dối. Vấn đề là “test đã pass” là một mệnh đề có vài chế độ hỏng mà nhìn trong bản tóm tắt thì giống hệt nhau: bộ test xanh nhưng một nửa bị skip; test khẳng định phần implementation chứ không khẳng định hành vi; test mock đúng cái thứ mà nó tuyên bố đang kiểm chứng; bộ test chưa từng thật sự chạy trên trạng thái cuối của code. Một báo cáo là một câu chữ. Một lần chạy xanh trong terminal của chính tôi là bằng chứng. Suy cho cùng, Phần 2 của series này nói về một hệ thống mà test có tồn tại và không ai nhận ra chúng không chạy.

Nên đây là tư thế test của toàn dự án, tất cả đều do tôi tự chạy lại:

Thành phầnTest
Backend (Kotlin / Spring Boot)57 test, độ phủ dòng khoảng 96%
Dashboard phụ huynh81 unit, 6 end-to-end
Ops console81 unit, 7 end-to-end
Companion app113 test
Marketing siteUnit test cộng Playwright

Ngoài việc chạy lại, đây là những thứ tôi đọc từng dòng chứ không lướt:

Ranh giới bảo mật. Từng kiểm tra phân quyền. Agent giỏi viết endpoint và kém tin cậy hơn ở chỗ cẩn thận xem người gọi có quyền với đúng các dòng của gia đình này hay không. Deny-by-default chỉ là thật nếu mọi truy vấn thực sự bị giới hạn phạm vi, và đó là việc một con người phải đọc từng endpoint. Đây là chỗ duy nhất tôi không tin gì cả.

Policy engine. Nó sinh ra lời buộc tội. Một vi phạm sai là một phụ huynh chất vấn con mình vì bug của tôi. Tôi tự đọc phần logic đó và viết thêm test quanh các ranh giới — lật qua nửa đêm, biên múi giờ, khung giờ ngủ vắt sang ngày hôm sau, một giới hạn chạm đúng bằng.

Xoá dữ liệu và retention. Lời hứa ở Phần 1 — export đầy đủ, xoá đầy đủ — bắt buộc phải đúng. Tôi tự kiểm chứng việc xoá trên database thật, kiểm tra rằng nó dọn đúng những gì nó tuyên bố trên mọi bảng, thay vì tin một cái test khẳng định rằng một hàm đã được gọi.

Bất cứ thứ gì chạm tới tiền hoặc secret. Cấu hình, xử lý token, những gì bị ghi log. Tôi tự grep secret trong history, vì phát hiện đầu tiên của đợt due diligence ở Phần 2 là credentials commit vào repo, và lặp lại chuyện đó sẽ rất nhục.

Phần concurrency trong pipeline. Việc giành dòng bằng SKIP LOCKED, bộ đếm retry, ngưỡng dead-letter. Bug concurrency thì pass test và hỏng ở production; tôi suy luận qua các nhánh trên giấy.

Cảm giác thật sự của việc này

Ít giống quản lý mấy bạn junior, giống làm technical lead cho một đội làm việc cực nhanh, không bao giờ mệt, đã đọc tài liệu của mọi thư viện, và không có phán đoán về cái gì mới quan trọng.

Phán đoán mới là công việc. Trong sáu mươi phát hiện đó, cái nào thành nguyên tắc. Có thêm Kafka không. Một lần capture hỏng một phần nên vẽ ra biểu đồ rỗng hay một cảnh báo. Đứa trẻ có được chế độ ẩn không. Không agent nào sinh ra bất kỳ quyết định nào trong số đó, và không quyết định nào là code.

Tóm tắt trung thực về phần tăng tốc: agent nén lại phần implementation, vốn có lẽ là một nửa công việc. Chúng không nén phần thiết kế, hợp đồng, kiểm chứng, hay các quyết định. Và chúng tạo ra một loại việc mới chưa từng có trước đây — review một khối lượng lớn code trông rất hợp lý, một kỹ năng thật sự khác và mệt hơn việc review một lượng code nhỏ từ một người mà bạn đã quen các kiểu sai của họ.

Nguyên tắc tôi rút ra

Agent viết code. Tôi chịu trách nhiệm về các tuyên bố.

Mọi con số trong series này là số tôi đã đo. Mọi số lượng test ở trên đến từ một lần chạy do tôi thực hiện. Sản phẩm này bán bằng niềm tin — nó giữ dữ liệu xem của trẻ em và nó nói với phụ huynh rằng con họ đã làm gì — và cái ngày tôi chuyển tiếp một tuyên bố mà mình chưa kiểm chứng là ngày tiền đề của sản phẩm trở thành lời nói dối.

Đó là kết thúc series năm phần về việc xây Kidslen: ý tưởng, đợt due diligence trở thành spec, kiến trúc và những thứ bị loại bỏ, cơ chế capture, và cách nó được xây. Sẽ còn thêm các bài build log khi mọi thứ hỏng, vì chúng sẽ hỏng.

Sản phẩm ở kidslen.app.

Xuất nội dung

Bình luận