Bỏ qua đến nội dung

Viết Kế Hoạch

Một kế hoạch tốt là tẻ nhạt. Không có bất ngờ, không có quyết định mờ ám, không có bước "để xem điều gì xảy ra".


Tại Sao Kế Hoạch Phải Đến Trước

Viết code mà không có kế hoạch là viết code hai lần. Lần đầu, bạn khám phá. Lần thứ hai, bạn sửa những gì bạn đã phá trong quá trình khám phá.

Một kế hoạch chuyển sự khám phá sang giai đoạn trước khi có code. Bạn đưa ra các quyết định kỹ thuật bằng ngôn ngữ con người, chi phí thấp, dễ thay đổi, trước khi bất kỳ code nào phải bị sửa đổi.

Kế hoạch trong Superpowers không phải là tài liệu kỹ thuật dài. Đây là một danh sách nhiệm vụ có cấu trúc — đủ cụ thể để một kỹ sư mới có thể thực thi từng bước trong 2–5 phút mà không cần hỏi câu hỏi nào.


Định Dạng Tiêu Đề Tài Liệu Kế Hoạch

Mỗi kế hoạch bắt đầu với một tiêu đề cung cấp context thiết yếu cho bất kỳ agent nào thực thi nó:

PLAN
----
Date: [Ngày kế hoạch được viết]
Branch: [feature/tên-nhánh]
Author: [Tên người/agent đã viết kế hoạch]
Status: PENDING | IN_PROGRESS | COMPLETE
Spec Reference: [Link hoặc mô tả ngắn về design spec]
Context: [2-3 câu tóm tắt những gì đang được xây dựng]
Scope: [Những gì trong phạm vi. Những gì ngoài phạm vi.]
Dependencies: [Bất kỳ công việc nào cần hoàn thành trước khi kế hoạch này có thể bắt đầu]

Tiêu đề này được điền vào trước khi viết bất kỳ nhiệm vụ nào. Một agent nhận kế hoạch này phải có đủ thông tin từ tiêu đề để hiểu những gì nó đang thực thi và tại sao.


Cấu Trúc Nhiệm Vụ

Mỗi nhiệm vụ trong kế hoạch tuân theo cùng một cấu trúc:

Task [N]: [Tên nhiệm vụ một dòng]
Status: TODO | IN_PROGRESS | DONE | BLOCKED
Estimated time: [2-5 phút]
File(s): [Đường dẫn file chính xác được thay đổi hoặc tạo]
What to do: [Mô tả rõ ràng bằng ngôn ngữ đơn giản về những gì nhiệm vụ này thực hiện]
Exact code: [Code chính xác cần viết, nếu áp dụng]
Exact command: [Lệnh shell chính xác để chạy, nếu áp dụng]
Verification: [Cách xác nhận nhiệm vụ này đã hoàn thành — một test, một kiểm tra thủ công, hoặc một output cụ thể]
Rollback: [Cách hoàn tác thay đổi này nếu nó phá vỡ một điều gì đó]

Mỗi trường tồn tại vì một lý do:

  • File(s): Loại bỏ sự mơ hồ về nơi làm việc
  • Exact code: Không có sự diễn giải. Agent không quyết định cái gì cần viết
  • Exact command: Lệnh cần chạy, chính xác như được gõ
  • Verification: Bằng chứng rằng nhiệm vụ hoàn thành — không phải suy luận
  • Rollback: Mọi thay đổi có thể được hoàn tác nếu nó gây ra một vấn đề

Quy Tắc Nhiệm Vụ 2–5 Phút

Một kỹ sư mới — người không biết codebase — có thể hoàn thành nhiệm vụ trong 2–5 phút mà không cần hỏi bất kỳ câu hỏi nào.

Nếu một nhiệm vụ cần hơn 5 phút, nó quá lớn. Chia nó ra.

Nếu một nhiệm vụ cần ít hơn 2 phút, nó có thể quá nhỏ — cân nhắc kết hợp nó với nhiệm vụ tiếp theo.

Quy tắc này tồn tại vì các nhiệm vụ lớn che giấu sự phức tạp. Một nhiệm vụ "Triển khai xác thực người dùng" là vô nghĩa. Một nhiệm vụ "Tạo file src/auth/validateToken.ts với hàm validateToken(token: string): boolean" là có thể thực thi.


DRY Trong Kế Hoạch

Đừng Lặp Lại Chính Mình áp dụng cho kế hoạch cũng như code. Nếu bạn thấy mình copy-paste một đoạn code hoặc lệnh giống hệt nhau qua nhiều nhiệm vụ, hãy trích xuất nó.

Ví dụ phổ biến về vi phạm DRY trong kế hoạch:

Task 3: Cài đặt dependencies
Exact command: npm install zod @prisma/client

Task 4: Chạy migration
Exact command: npm install zod @prisma/client && npx prisma migrate dev

Task 4 lặp lại lệnh cài đặt mà không cần thiết. Nó nên phụ thuộc vào Task 3.

DRY trong kế hoạch: tham chiếu kết quả của các nhiệm vụ trước thay vì lặp lại nội dung của chúng.


YAGNI Trong Kế Hoạch

You Aren't Gonna Need It — Đừng lên kế hoạch cho những gì spec không yêu cầu.

Các vi phạm YAGNI phổ biến trong kế hoạch:

  • Xử lý lỗi không có trong spec: Nếu spec không đề cập đến trường hợp đó, đừng lên kế hoạch cho nó
  • Abstraction cho việc sử dụng trong tương lai: Nếu không có trường hợp sử dụng thứ hai, đừng tạo ra một generic interface
  • Logging thêm vào: Log những gì spec yêu cầu, không phải những gì bạn nghĩ có thể hữu ích
  • Hệ thống cấu hình: Nếu spec mô tả một giá trị cố định, đừng biến nó thành một tham số cấu hình

Bất cứ khi nào bạn thêm một nhiệm vụ, hãy hỏi: Thiết kế được phê duyệt có yêu cầu rõ ràng điều này không? Nếu không, loại bỏ nó.


TDD Trong Kế Hoạch

Mỗi nhiệm vụ implementation phải được đi kèm với một nhiệm vụ test. Cấu trúc:

Task N:   Viết failing test cho [hành vi]     ← RED
Task N+1: Implement [hành vi]                  ← GREEN

Không có task implementation nào có thể tồn tại mà không có một task test tương ứng đứng trước nó. Đây không phải là một ưu tiên — đó là một ràng buộc. Nếu bạn thấy mình viết một task implementation mà không có task test, hãy thêm task test trước.

Điều này áp dụng ngay cả với những thay đổi tưởng chừng nhỏ. "Thêm một field mới vào một model" cần một test xác minh field tồn tại và hoạt động đúng.


Quản Lý Scope

Kế hoạch là nơi scope creep xảy ra. Khi bạn đang viết các nhiệm vụ, bạn phát hiện ra các trường hợp đặc biệt và edge case. Bạn nhận ra rằng thực sự sẽ tốt nếu cũng xử lý X. Bạn muốn code trở nên robust hơn.

Hầu hết thời gian, điều này là scope creep. Và bạn phải cưỡng lại nó.

Tín Hiệu Scope Creep

Ngừng lại và đặt câu hỏi khi bạn thấy mình:

  • Thêm các nhiệm vụ không xuất hiện trong session brainstorming
  • Sử dụng ngôn ngữ "trong khi chúng ta đang làm điều này, chúng ta cũng nên..."
  • Chỉnh sửa các file không liên quan đến tính năng
  • Ước tính số nhiệm vụ tăng lên hơn gấp đôi so với kế hoạch ban đầu

Tín Hiệu Giảm Scope

Tìm kiếm các nhiệm vụ:

  • Trùng lặp với nhau
  • Nice-to-have thay vì must-have
  • Có thể được trì hoãn sang một kế hoạch riêng biệt
  • Kiểm thử các trường hợp không được đề cập trong spec

Review Kế Hoạch Trước Khi Thực Thi

Trước khi thực thi bất kỳ nhiệm vụ nào, một kế hoạch phải vượt qua ba giai đoạn review:

Giai Đoạn 1: Kiểm Tra Tính Đầy Đủ

  • Tất cả các yêu cầu từ spec có được bao gồm không?
  • Có bất kỳ yêu cầu nào bị thiếu không?
  • Là tiêu đề đầy đủ (ngày, branch, tác giả, trạng thái, tham chiếu spec, context, scope, dependencies)?

Giai Đoạn 2: Kiểm Tra Alignment Scope

  • Mỗi nhiệm vụ có thể được truy về spec không?
  • Có nhiệm vụ nào không có trong spec không?
  • Có dấu hiệu nào của scope creep không?

Giai Đoạn 3: Kiểm Tra Kích Thước Nhiệm Vụ

  • Mỗi nhiệm vụ có hoàn thành được trong 2–5 phút không?
  • Có nhiệm vụ nào quá vague để thực thi không?
  • Có cặp test/implementation nào cho mỗi behavior không?

Nếu bất kỳ giai đoạn nào thất bại, hãy sửa kế hoạch trước khi tiến hành. Một kế hoạch tệ không được cải thiện bằng cách thực thi nó nhanh hơn.


Ví Dụ: Kế Hoạch Đơn Giản

PLAN
----
Date: 2026-03-15
Branch: feature/order-ship-email
Author: Developer
Status: PENDING
Spec Reference: Brainstorm session 2026-03-14 - gửi email xác nhận khi đơn hàng được giao
Context: Khi một đơn hàng được đánh dấu là "shipped", hệ thống gửi email xác nhận
  cho khách hàng bao gồm order ID và ngày dự kiến.
Scope: IN: trigger email, template email, delivery via SendGrid
       OUT: email tracking, retry logic, bounce handling
Dependencies: SendGrid API key phải tồn tại trong env

Task 1: Viết failing test cho sendShipmentEmail
Status: TODO
Estimated time: 3 phút
File(s): src/email/__tests__/sendShipmentEmail.test.ts
What to do: Tạo test xác minh sendShipmentEmail được gọi với đúng params khi
  order được đánh dấu là shipped
Exact code: |
  test('sends shipment email when order status changes to shipped', async () => {
    const mockSend = jest.fn().mockResolvedValue({ id: 'msg_123' });
    jest.mock('../sendgrid', () => ({ send: mockSend }));
    await updateOrderStatus('order_456', 'shipped');
    expect(mockSend).toHaveBeenCalledWith(expect.objectContaining({
      to: 'customer@example.com',
      subject: expect.stringContaining('order_456'),
    }));
  });
Exact command: npm test -- sendShipmentEmail
Verification: Test thất bại với "sendShipmentEmail is not a function" hoặc tương tự
Rollback: Xóa file test

Task 2: Implement sendShipmentEmail
Status: TODO
Estimated time: 4 phút
File(s): src/email/sendShipmentEmail.ts
What to do: Tạo function sendShipmentEmail nhận orderId và customerEmail,
  gửi email qua SendGrid với template shipment confirmation
Exact code: |
  import { send } from './sendgrid';
  export async function sendShipmentEmail(orderId: string, customerEmail: string) {
    return send({
      to: customerEmail,
      from: 'orders@example.com',
      subject: `Your order ${orderId} has shipped`,
      text: `Your order ${orderId} is on its way.`,
    });
  }
Exact command: npm test -- sendShipmentEmail
Verification: Test từ Task 1 pass
Rollback: git revert HEAD

Task 3: Viết failing test cho order status hook
Status: TODO
Estimated time: 3 phút
File(s): src/orders/__tests__/orderStatusHook.test.ts
What to do: Test xác minh hook gọi sendShipmentEmail khi trạng thái thay đổi sang "shipped"
Exact code: [xem file test đầy đủ trong spec]
Exact command: npm test -- orderStatusHook
Verification: Test thất bại với "sendShipmentEmail is not defined"
Rollback: Xóa file test

Task 4: Wire hook vào order status update
Status: TODO
Estimated time: 3 phút
File(s): src/orders/updateOrderStatus.ts
What to do: Import và gọi sendShipmentEmail khi newStatus === 'shipped'
Exact code: [xem diff trong spec]
Exact command: npm test
Verification: Tất cả test pass; không có test mới nào thất bại
Rollback: git revert HEAD

Đây là một kế hoạch nhàm chán. Điều đó là đúng. Không có bất ngờ. Không có quyết định mờ ám. Chỉ là các bước rõ ràng với kết quả có thể xác minh.


Một kế hoạch tốt là nhàm chán. Nếu kế hoạch của bạn thú vị, đó là vì nó ẩn giấu sự phức tạp. Phức tạp ẩn giấu là phức tạp phát nổ trong quá trình implementation.