Bạn không thể tạm dừng việc phát triển. Đó là điều đầu tiên cần phải chấp nhận. Các ticket vẫn liên tục đổ về, khách hàng mong đợi các lô hàng, và mã nguồn hiện tại của bạn không dừng chạy chỉ vì bạn quyết định viết tài liệu cho nó. Không một quản lý kỹ thuật (engineering manager) nào phê duyệt một đợt đóng băng kéo dài cả tháng chỉ để đội ngũ viết bản đặc tả lẽ ra phải có ngay từ ngày đầu tiên. OpenSpec được xây dựng cho thực tế, không phải cho những ảo tưởng về dự án mới (greenfield). Nó hoạt động hiệu quả nhất khi bạn gắn nó vào những gì bạn đã có, bao gồm cả khách hàng.
Mục tiêu ở đây không phải là viết lại toàn bộ. Đó là một cuộc "khảo cổ trung thực". Bạn đào bới những gì thực sự đang chạy trên môi trường production, mô tả chúng một cách chính xác, và để mô tả đó tiến hóa cùng với mã nguồn của bạn. Khi bản đặc tả khớp với hệ thống, bạn sẽ giúp cuộc sống của các kỹ sư gia nhập vào quý tới trở nên dễ dàng hơn, cũng như hỗ trợ các công cụ AI đang nằm ngay trong IDE của bạn. Đây là cách thực hiện mà không bỏ lỡ bất kỳ đợt phát hành (release) nào.
Bắt đầu với những gì bạn thực sự làm
Mở kho lưu trữ (repository) của bạn ra, bạn sẽ thấy các thư mục có tên controllers, models, services, và utils. Đó là các lớp kỹ thuật, và chúng đang "lừa dối" bạn. Chúng không mô tả hệ thống của bạn làm gì cho doanh nghiệp. Một thư mục đầy các tệp JavaScript không giải thích được cách một đơn hàng trở thành một lô hàng. Để áp dụng OpenSpec, bạn cần tư duy theo các năng lực nghiệp vụ (capabilities).
Hãy tìm kiếm các hoạt động kinh doanh ổn định, những thứ sẽ vẫn tồn tại ngay cả khi bạn viết lại toàn bộ stack bằng một ngôn ngữ khác. Trong hầu hết các công ty sản phẩm, chúng xuất hiện lặp đi lặp lại: Đơn hàng (Orders), Thanh toán (Billing), Kho hàng (Inventory), Khách hàng (Customers), và Thông báo (Notifications). Hãy liệt kê từ năm đến tám năng lực cốt lõi này.
Với mỗi năng lực, hãy buộc bản thân phải trả lời năm câu hỏi cụ thể. Năng lực này giải quyết vấn đề thực tế nào? Mã nguồn thực sự nằm ở đâu—một service, ba microservices, hay một module cũ (legacy) mà không ai muốn chạm vào? Điều gì kích hoạt nó: một cú nhấp chuột của người dùng, một cron job định kỳ, hay một inbound webhook? Dữ liệu đầu vào là gì và dữ liệu đầu ra là gì? Và cuối cùng, những hệ thống nào khác phụ thuộc vào nó, nghĩa là điều gì sẽ hỏng nếu phần này ngừng hoạt động?
Hãy thành thật một cách tàn nhẫn. Nếu năng lực "Khách hàng" của bạn nằm rải rác trên một Rails monolith, một Node API và một CRM bên ngoài, hãy viết chính xác như vậy. Bản đồ của bạn phải giống như địa hình thực tế, chứ không phải là giấc mơ của một kiến trúc sư.
Viết sự thật, đừng viết danh sách mong muốn
Câu nói nguy hiểm nhất trong bất kỳ nỗ lực làm tài liệu nào là: "Trong khi đang viết cái này, chúng ta cũng nên sửa nó luôn đi." Dừng lại ngay. Bạn không phải đang thiết kế lại quy trình thanh toán. Bạn đang mô tả quy trình thanh toán hiện đang thực hiện trừ tiền thẻ tín dụng thật ngay lúc này.
Nếu việc đặt hàng kích hoạt một lệnh thu tiền ngay lập tức và sau đó gửi email thông qua một background worker, hãy tài liệu hóa chính xác trình tự đó. Đừng chèn thêm một hàng đợi sự kiện (event queue) mà bạn dự định thêm vào quý tới. Đừng giả vờ rằng việc xác thực diễn ra ở API edge nếu thực tế nó nằm sâu bên trong một service class. Sự chính xác quan trọng hơn nhiều so với sự kỳ vọng.
Tài liệu sai lệch còn tệ hơn là không có tài liệu. Nó huấn luyện nhân viên mới kỳ vọng vào những hành vi không tồn tại. Nó dẫn dắt các trợ lý lập trình AI đi vào những con đường tưởng tượng dựa trên những mong muốn hão huyền. Khi bản đặc tả của bạn khớp với production, bạn tạo ra một nền tảng đáng tin cậy. Việc gỡ lỗi (debugging) sẽ nhanh hơn vì bạn không còn phải đoán mò về luồng chạy "dự kiến" nữa. Việc tái cấu trúc (refactoring) sẽ an toàn hơn vì bạn biết điểm bắt đầu là có thật.
Trích xuất các hợp đồng từ API của bạn
Các API endpoint của bạn đã thực thi các quy tắc rồi. Chúng chỉ đang để chúng ở dạng ngầm định. Áp dụng OpenSpec có nghĩa là đưa những quy tắc đó ra ánh sáng.
Bắt đầu với đầu vào và việc xác thực (validation). Endpoint đó thực sự chấp nhận những gì? Hãy tài liệu hóa các kiểu dữ liệu (types), các trường bắt buộc, độ dài tối đa và các phụ thuộc giữa các trường. Sau đó, hãy mô tả hành vi nghiệp vụ. Lời gọi này tạo ra một bản ghi, kích hoạt một tác dụng phụ (side effect), hay chỉ đơn giản là xác thực trạng thái với một dịch vụ khác? Hãy cụ thể.
Cuối cùng, hãy lập danh mục các phản hồi (responses). Thành công sẽ trả về cái gì? Các mã lỗi chính xác là gì và chúng xuất hiện trong điều kiện nào? Đừng viết "trả về một lỗi". Hãy viết "trả về 422 khi thiếu địa chỉ thanh toán và 409 khi kho hàng đã được một tiến trình khác giữ chỗ". Mức độ chính xác đó sẽ biến một route mơ hồ thành một hợp đồng (contract) mà các đội frontend, kỹ sư QA và các công cụ tự động có thể tin tưởng.
Săn tìm các quy tắc ẩn
Some of the most expensive knowledge in your system lives in the gaps. It is buried in conditional blocks inside service classes, tucked into database triggers, or written into stored procedures that no one has touched in two years. These are your business rules, and they are usually rediscovered during outages or by cornering the one engineer who has been there since the beginning.
Pull them into daylight. Start with the ones you already know. Orders above a certain value need manager approval before they proceed. Inactive user accounts cannot create new orders. Refunds are only permitted before settlement completes. Write each rule next to the capability it governs, in language clear enough that a product manager could read it without a translator.
When you centralize these rules, you do more than document them. You expose duplication. You reveal conflicts. And you give the entire team a single place to debate policy before someone commits a one-line change that accidentally violates a constraint you forgot existed.
Map the Plumbing
Modern systems run on events. An action in one service ripples through half a dozen others before anything visible reaches the user. You need to chart those ripples. Map the flow from one event to the next for your core workflows. Order created leads to inventory reserved, which waits for payment confirmed. Draw the full chain, even if some links feel fragile or use different protocols.
Do not stop at internal traffic. External services are part of your system whether you treat them that way or not. For each integration, record its purpose, how your application authenticates, and how it fails. Does the payment gateway timeout after thirty seconds and return a generic 500? Does the shipping API return malformed JSON on weekends? Does the identity provider revoke refresh tokens earlier than its own documentation claims? These details look trivial
