Claude Code cho scaffold API Go: handlers và services nhất quán
Claude Code cho scaffold Go API: xác định mẫu handler-service-error rõ ràng, rồi sinh endpoint mới giữ tính nhất quán trên toàn bộ API Go của bạn.

Tại sao API Go trở nên lộn xộn khi không cố định pattern sớm
API Go thường bắt đầu sạch: vài endpoint, một hai người, và mọi thứ sống trong đầu mọi người. Rồi API lớn dần, tính năng ra gấp, và những khác biệt nhỏ len lỏi. Mỗi cái một mình có vẻ vô hại, nhưng cộng lại thì làm chậm mọi thay đổi sau này.
Một ví dụ phổ biến: một handler decode JSON vào struct và trả 400 với thông tin hữu ích, handler khác trả 422 với cấu trúc khác, handler thứ ba log lỗi theo định dạng khác. Không cái nào làm vỡ biên dịch. Chúng chỉ tạo ra quyết định liên tục và sửa lại nhỏ mỗi khi bạn thêm thứ gì đó.
Bạn sẽ thấy sự lộn xộn ở những chỗ như:
- Thân lỗi khác nhau giữa các endpoint, khiến client phải có ngoại lệ.
- Service đôi khi trả lỗi thô từ DB và đôi khi trả lỗi "thân thiện".
- Tên gọi trôi dạt (
CreateUser,AddUser,RegisterUser) khiến tìm kiếm khó khăn. - Validation nhảy lung tung, khiến các bug cũ lặp lại.
"Scaffolding" ở đây nghĩa là một khuôn mẫu có thể lặp lại cho công việc mới: code đặt ở đâu, mỗi layer làm gì, và phản hồi trông như thế nào. Ít hơn là sinh nhiều code, và nhiều hơn là khóa một hình dạng nhất quán.
Công cụ như Claude có thể giúp bạn scaffold endpoint nhanh, nhưng chúng chỉ hữu ích khi bạn coi pattern là quy tắc. Bạn định nghĩa quy tắc, review mọi diff, và chạy test. Mô hình chỉ điền phần chuẩn; nó không được phép định nghĩa lại kiến trúc của bạn.
Chọn một phân tách lớp: handler, service, và data access
API Go dễ mở rộng khi mọi request theo cùng một đường đi. Trước khi bắt đầu sinh endpoint, chọn một phân tách lớp và tuân theo nó.
Trách nhiệm, giữ đơn giản
Nhiệm vụ của handler chỉ là HTTP: đọc request, gọi service, và ghi response. Nó không nên chứa quy tắc nghiệp vụ, SQL, hoặc logic "chỉ trường hợp này".
Service chịu trách nhiệm use case: quy tắc nghiệp vụ, quyết định, và phối hợp giữa repository hoặc cuộc gọi ngoài. Nó không nên biết các mối quan tâm HTTP như status code, header, hay cách hiển thị lỗi.
Data access (repository/store) chịu chi tiết persistency. Nó dịch ý định service thành SQL/query/transaction. Nó không nên áp dụng quy tắc nghiệp vụ ngoài tính toàn vẹn dữ liệu cơ bản, và không nên định hình phản hồi API.
Một checklist phân tách giữ thực tế:
- Handler: parse input, gọi service, map lỗi sang HTTP, ghi JSON
- Service: áp dụng quy tắc nghiệp vụ, gọi repo, trả domain result hoặc lỗi typed
- Data access: thực thi query, map rows sang structs, trả lỗi lưu trữ
- Shared: types lỗi chung và helper phản hồi
- Không có layer nào nhảy qua layer khác (handler không bao giờ gọi repo trực tiếp)
Một quy tắc cho validation
Chọn một quy tắc và đừng bẻ cong.
Một cách đơn giản:
- Handlers làm "shape validation" (trường bắt buộc, định dạng cơ bản).
- Services làm "meaning validation" (quyền, bất biến, trạng thái).
Ví dụ: handler kiểm tra email có tồn tại và nhìn như email. Service kiểm tra email được phép và chưa được dùng.
Những gì chuyển giữa các layer
Quyết định sớm xem service trả domain types hay DTOs.
Mặc định sạch: handlers dùng request/response DTOs, services dùng domain types, và handler map domain sang response. Điều đó giữ service ổn định ngay cả khi hợp đồng HTTP thay đổi.
Nếu mapping cảm thấy nặng, vẫn giữ nhất quán: để service trả domain type cộng lỗi typed, và giữ việc định dạng JSON trong handler.
Định nghĩa phản hồi lỗi chuẩn và bản đồ mã trạng thái
Nếu bạn muốn các endpoint sinh tự động giống như cùng một người viết, khóa định dạng lỗi sớm. Sinh hoạt tốt nhất khi định dạng đầu ra không thể thương lượng: một shape JSON, một bản đồ status code, và một quy tắc cho những gì được tiết lộ.
Bắt đầu với một error envelope duy nhất mà mọi endpoint trả khi thất bại. Giữ nó nhỏ và dễ đoán:
{
"code": "validation_failed",
"message": "One or more fields are invalid.",
"details": {
"fields": {
"email": "must be a valid email address",
"age": "must be greater than 0"
}
},
"request_id": "req_01HR..."
}
Dùng code cho máy (ổn định và dễ dự đoán) và message cho con người (ngắn và an toàn). Đặt dữ liệu cấu trúc vào details. Với validation, một details.fields map đơn giản dễ sinh và dễ cho client hiển thị cạnh input.
Tiếp theo, viết ra bản đồ status code và tuân theo nó. Càng ít tranh luận cho mỗi endpoint, càng tốt. Nếu bạn muốn cả 400 và 422, hãy phân chia rõ ràng:
bad_json-> 400 Bad Request (JSON bị hỏng)validation_failed-> 422 Unprocessable Content (JSON đúng định dạng, nhưng trường không hợp lệ)not_found-> 404 Not Foundconflict-> 409 Conflict (khóa trùng, mismatch version)unauthorized-> 401 Unauthorizedforbidden-> 403 Forbiddeninternal-> 500 Internal Server Error
Quyết định cái gì log vs cái gì trả về. Một quy tắc tốt: client nhận thông điệp an toàn và request ID; logs chứa lỗi đầy đủ và context nội bộ (SQL, payload upstream, user IDs) mà bạn không bao giờ muốn rò rỉ.
Cuối cùng, chuẩn hóa request_id. Chấp nhận header ID đến nếu có (từ API gateway), nếu không thì tạo một cái ở edge (middleware). Gắn nó vào context, kèm vào logs, và trả lại trong mọi phản hồi lỗi.
Cấu trúc thư mục và tên file khiến việc sinh predictable
Nếu muốn scaffold giữ nhất quán, cấu trúc thư mục của bạn phải nhàm chán và có thể lặp lại. Generator theo các pattern chúng nhìn thấy, nhưng sẽ trôi khi file rải rác hoặc tên thay đổi theo feature.
Chọn một convention đặt tên và không bẻ cong. Chọn một từ cho mỗi thứ và giữ nó: handler, service, repo, request, response. Nếu route là POST /users, đặt tên file và type quanh users và create (không thỉnh thoảng register, thỉnh thoảng addUser).
Một layout đơn giản phù hợp các lớp thường thấy:
internal/
httpapi/
handlers/
users_handler.go
services/
users_service.go
data/
users_repo.go
apitypes/
users_types.go
Quyết định nơi các type chia sẻ nằm, vì đó là nơi dự án thường trở nên rối. Một quy tắc hữu ích:
- API request/response types sống ở
internal/apitypes(khớp JSON và nhu cầu validation). - Domain types sống gần layer service hơn (khớp quy tắc nghiệp vụ).
Nếu một type có tag JSON và được thiết kế cho client, coi nó là API type.
Giữ dependency của handler ở mức tối thiểu và làm quy tắc đó rõ ràng:
- Handlers import chỉ: routing/http, context, apitypes, và services
- Services import: domain types và data access
- Data access import: database driver và query helpers
- Không handler nào import package database trực tiếp
Viết một tài liệu pattern ngắn ở root repo (Markdown plain là đủ). Bao gồm cây thư mục, quy tắc đặt tên, và một flow ví dụ nhỏ (handler -> service -> repo, kèm file mỗi phần). Đây là tài liệu tham chiếu chính xác bạn dán vào generator để các endpoint mới luôn trùng cấu trúc.
Tạo một endpoint tham chiếu làm pattern
Trước khi sinh mười endpoint, tạo một endpoint bạn tin tưởng. Đây là chuẩn vàng: file bạn trỏ vào và nói, "Code mới phải trông như này." Bạn có thể viết nó từ đầu hoặc refactor một cái sẵn cho đến khi khớp.
Giữ handler mỏng. Một cách giúp nhiều: đặt một interface giữa handler và service để handler phụ thuộc vào contract, không phải struct cụ thể.
Thêm vài comment ngắn trong endpoint tham chiếu chỉ nơi code sinh sau này có thể vấp. Giải thích quyết định (tại sao 400 vs 422, tại sao create trả 201, tại sao che lỗi nội bộ sau message chung). Bỏ comment chỉ tóm tắt code.
Khi endpoint tham chiếu hoạt động, trích helpers ra để mọi endpoint mới có ít cơ hội trôi. Helpers tái sử dụng phổ biến nhất thường là:
- Bind JSON và xử lý body malformed
- Validate input và trả lỗi field
- Ghi JSON response nhất quán
- Map lỗi domain sang status code HTTP
Đây là ví dụ "handler mỏng + interface":
type UserService interface {
CreateUser(ctx context.Context, in CreateUserInput) (User, error)
}
func (h *Handler) CreateUser(w http.ResponseWriter, r *http.Request) {
var in CreateUserRequest
if err := BindJSON(r, &in); err != nil {
WriteError(w, ErrBadJSON) // 400: malformed JSON
return
}
if err := Validate(in); err != nil {
WriteError(w, err) // 422: validation details
return
}
user, err := h.svc.CreateUser(r.Context(), in.ToInput())
if err != nil {
WriteError(w, err)
return
}
WriteJSON(w, http.StatusCreated, user)
}
Khóa pattern đó bằng vài test (thậm chí một table test nhỏ cho mapping lỗi). Generator làm việc tốt nhất khi có một mục tiêu sạch để bắt chước.
Từng bước: dùng Claude để sinh endpoint mới khớp pattern
Tính nhất quán bắt đầu từ những gì bạn dán vào và cái bạn cấm. Với endpoint mới, cung cấp hai thứ:
- Endpoint tham chiếu của bạn (ví dụ "hoàn hảo")
- Một chú thích pattern ngắn nêu tên package, function, và helper phải theo
1) Cung cấp context trước (tham chiếu + quy tắc)
Bao gồm handler, method service, request/response types, và helper chia sẻ endpoint dùng. Rồi nêu hợp đồng bằng từ ngữ rõ ràng:
- Route + method (ví dụ:
POST /v1/widgets) - Trường JSON request (bắt buộc vs tuỳ chọn)
- Hình dạng JSON response
- Các case lỗi và status code
- File bạn mong nhận lại (và chỉ những file đó)
Nói rõ phải khớp: tên, đường dẫn package, và helper functions (WriteJSON, BindJSON, WriteError, validator của bạn).
2) Yêu cầu output ở đúng hình dạng bạn muốn
Một prompt chặt giúp tránh "refactor hữu ích". Ví dụ:
Using the reference endpoint below and the pattern notes, generate a new endpoint.
Contract:
- Route: POST /v1/widgets
- Request: {"name": string, "color": string}
- Response: {"id": string, "name": string, "color": string, "createdAt": string}
- Errors: invalid JSON -> 400; validation -> 422; duplicate name -> 409; unexpected -> 500
Output ONLY these files:
1) internal/http/handlers/widgets_create.go
2) internal/service/widgets.go (add method only)
3) internal/types/widgets.go (add types only)
Do not change: router setup, existing error format, existing helpers, or unrelated files.
Must use: package paths and helper functions exactly as in the reference.
Nếu bạn dùng tests, yêu cầu rõ ràng (và đặt tên file test). Nếu không, mô hình có thể bỏ qua test hoặc sáng tạo một setup test khác.
Làm một kiểm tra diff nhanh sau khi sinh. Nếu nó sửa helper chung, đăng ký router, hoặc định dạng lỗi chuẩn, từ chối đầu ra và nhắc lại quy tắc "không thay đổi" chặt hơn.
Mẫu prompt tái sử dụng cho scaffold endpoint nhất quán
Đầu vào chỉ nhất quán bằng chính đầu vào của bạn. Cách nhanh nhất tránh code "gần đúng" là dùng một prompt template mỗi lần, với một snapshot ngắn từ repo.
Template prompt
Dán, điền chỗ trống:
You are editing an existing Go HTTP API.
CONTEXT
- Folder tree (only the relevant parts):
<paste a small tree: internal/http, internal/service, internal/repo, etc>
- Key types and patterns:
- Handler signature style: <example>
- Service interface style: <example>
- Request/response DTOs live in: <package>
- Standard error response JSON:
{
"error": {
"code": "invalid_argument",
"message": "...",
"details": {"field": "reason"}
}
}
- Status code map:
invalid_json -> 400
invalid_argument -> 422
not_found -> 404
conflict -> 409
internal -> 500
TASK
Add a new endpoint: <METHOD> <PATH>
- Handler name: <Name>
- Service method: <Name>
- Request JSON example:
{"name":"Acme"}
- Success response JSON example:
{"id":"123","name":"Acme"}
CONSTRAINTS
- No new dependencies.
- Keep functions small and single-purpose.
- Match existing naming, folder layout, and error style exactly.
- Do not refactor unrelated files.
ACCEPTANCE CHECKS
- Code builds.
- Existing tests pass (add tests only if the repo already uses them for handlers/services).
- Run gofmt on changed files.
FINAL INSTRUCTION
Before writing code, list any assumptions you must make. If an assumption is risky, ask a short question instead.
Cái này hiệu quả vì bắt buộc ba thứ: context block (cái đang có), constraints block (cái không được làm), và ví dụ JSON cụ thể (để shape không trôi). Final instruction là tấm lưới an toàn: nếu mô hình không chắc, nó sẽ hỏi trước khi ghi mã.
Ví dụ thực tế: thêm endpoint Create mà không phá phong cách
Giả sử bạn muốn thêm một endpoint "Create project". Mục tiêu: nhận name, áp vài quy tắc, lưu, và trả ID mới. Khó là giữ split handler-service-repo và kiểu lỗi JSON bạn đang dùng.
Một luồng nhất quán trông như:
- Handler: bind JSON, validate cơ bản, gọi service
- Service: áp quy tắc nghiệp vụ (như unique), gọi repo, trả domain result
- Repo: ghi vào Postgres, trả ID tạo ra
Yêu cầu handler chấp nhận:
{ "name": "Roadmap", "owner_id": "u_123" }
Thành công trả 201 Created. ID nên đến từ một nguồn duy nhất mỗi lần. Ví dụ: để Postgres generate và repo trả lại:
{ "id": "p_456", "name": "Roadmap", "owner_id": "u_123", "created_at": "2026-01-09T12:34:56Z" }
Hai đường thất bại thực tế:
Nếu validation fail (name thiếu hoặc quá ngắn), trả lỗi trường theo shape chuẩn và status bạn chọn:
{ "error": { "code": "VALIDATION_ERROR", "message": "Invalid request", "details": { "name": "must be at least 3 characters" } } }
Nếu tên phải unique per owner và service thấy project tồn tại, trả 409 Conflict:
{ "error": { "code": "PROJECT_NAME_TAKEN", "message": "Project name already exists", "details": { "name": "Roadmap" } } }
Một quyết định giữ pattern sạch: handler kiểm tra "request shaped đúng chưa?" còn service sở hữu "được phép không?". Sự tách này làm cho các endpoint sinh tự động dễ đoán.
Sai lầm thường gặp phá tính nhất quán (và cách tránh)
Cách nhanh nhất làm mất nhất quán là để generator tùy cơ ứng biến.
Một drift thường thấy là shape lỗi mới. Một endpoint trả {error: "..."}, endpoint khác trả {message: "..."}, endpoint thứ ba thêm nested object. Khóa điều này bằng một error envelope duy nhất và một bản đồ status code trong một nơi, rồi yêu cầu endpoint mới tái sử dụng chúng bằng import path và tên hàm. Nếu generator đề xuất field mới, coi đó là thay đổi API — phải làm change request, không phải tiện lợi.
Drift khác là handler phình to. Nó bắt đầu nhỏ: validate, rồi check permission, rồi query DB, rồi branching nghiệp vụ. Giữ một quy tắc: handler dịch HTTP thành input/output typed; service sở hữu quyết định; data access chịu query.
Mismatch đặt tên cũng tích tụ. Nếu một endpoint dùng CreateUserRequest và endpoint khác dùng NewUserPayload, bạn mất thời gian nối loại và viết glue. Chọn một quy ước tên và bác bỏ tên mới nếu không có lý do chính đáng.
Không bao giờ trả raw database errors cho client. Ngoài việc rò rỉ, nó tạo thông điệp lỗi và mã trạng thái không nhất quán. Bọc lỗi nội bộ, log nguyên nhân, và trả code public ổn định.
Tránh thêm thư viện mới "vì tiện". Mỗi validator, helper router, hoặc gói lỗi thêm vào là một phong cách khác phải khớp.
Những guardrail ngăn phần lớn hỏng hóc:
- Yêu cầu endpoint mới tái sử dụng error types và helper hiện có.
- Giữ handler không có quy tắc nghiệp vụ và truy cập DB.
- Thực thi một quy ước đặt tên cho request/response structs.
- Map lỗi nội bộ sang mã lỗi public, không trả raw error.
- Thêm dependency chỉ khi có lý do rõ ràng và viết ra.
Nếu bạn không thể diff hai endpoint và thấy cùng hình dạng (imports, flow, error handling), thắt chặt prompt và sinh lại trước khi merge.
Checklist nhanh trước khi merge endpoint sinh tự động
Trước khi merge bất cứ thứ gì sinh tự động, kiểm tra cấu trúc trước. Nếu hình dạng đúng, bug logic dễ phát hiện hơn.
Kiểm tra cấu trúc:
- Flow handler nhất quán: bind input, validate, gọi service, map lỗi domain sang HTTP, ghi response.
- Code service chỉ chứa quy tắc nghiệp vụ: không có HTTP hay JSON work trực tiếp.
- Response thành công khớp house style (dùng envelope chung hay trực tiếp JSON đều thống nhất).
- Phản hồi lỗi đồng bộ: cùng trường JSON, cùng mã, cùng hành vi
request_id. - Tên và vị trí nhàm chán: tên file, function, route khớp endpoint hiện có, và mọi file đều gofmt-formatted.
Kiểm tra hành vi:
- Chạy test và thêm tối thiểu một test nhỏ cho nhánh handler/service mới.
- Xác nhận failure validation trả cùng mã và status như endpoint tương tự.
- Kích hoạt một lỗi service đã biết (ví dụ "not found" hoặc "conflict") và xác nhận status HTTP và shape JSON.
- Quét tìm copy-paste leftovers: route sai, log message sai, tên DTO mismatch.
- Build và chạy server local một lần để đảm bảo wiring và imports đúng.
Bước tiếp theo: chuẩn hoá pattern, rồi scale generation an toàn
Đối xử pattern của bạn như một hợp đồng chung, không phải sở thích. Giữ tài liệu "how we build endpoints" cạnh code, và duy trì một endpoint tham chiếu đầy đủ để minh họa toàn bộ cách làm.
Mở rộng tạo tự động theo từng lô nhỏ. Sinh 2–3 endpoint chạm các cạnh khác nhau (một GET đơn giản, một create có validation, một update có case not-found). Rồi dừng và tinh chỉnh. Nếu review liên tục thấy drift cùng kiểu, cập nhật baseline doc và endpoint tham chiếu trước khi sinh tiếp.
Vòng lặp bạn có thể lặp:
- Ghi baseline: tên file, tên function, structs request/response, mã lỗi, và nơi validation xảy ra.
- Giữ một endpoint "vàng" và cập nhật nó đầu tiên khi pattern thay đổi.
- Sinh theo lô vài endpoint, review cho nhất quán, rồi chỉnh prompt và doc pattern.
- Refactor các endpoint cũ thành từng cụm và giữ phương án rollback nếu hành vi thay đổi.
- Theo dõi một chỉ số trong một tuần: thời gian để thêm một endpoint, tỷ lệ bug sau merge, hoặc thời gian review.
Nếu muốn vòng build-review khít hơn, một nền tảng vibe-coding như Koder.ai (koder.ai) có thể giúp scaffold và lặp nhanh trong workflow chat-driven, rồi export source khi nó khớp chuẩn của bạn. Công cụ ít quan trọng hơn quy tắc: baseline của bạn là người dẫn đường.
Câu hỏi thường gặp
Cách nhanh nhất để ngăn API Go trở nên không nhất quán là gì?
Khóa một khuôn mẫu có thể lặp lại ngay từ đầu: một tách lớp nhất quán (handler → service → data access), một phong cách envelope lỗi duy nhất, và một bản đồ mã trạng thái. Sau đó dùng một “endpoint tham chiếu” duy nhất làm ví dụ mà mọi endpoint mới phải sao chép.
Handler nên làm gì (và không nên làm gì)?
Giữ handler chỉ lo về HTTP:
- Bind/parse request
- Thực hiện "shape validation" cơ bản (trường bắt buộc, định dạng đơn giản)
- Gọi service
- Map lỗi typed sang mã HTTP
- Ghi JSON bằng helper chung
Nếu bạn thấy SQL, kiểm tra quyền, hoặc branching nghiệp vụ xuất hiện trong handler, hãy chuyển chúng vào service.
Những gì nên nằm trong layer service?
Đặt các quy tắc nghiệp vụ và quyết định vào service:
- Quyền truy cập và kiểm soát
- Bất biến (state transitions, quy tắc duy nhất, kiểm tra "được phép")
- Orchestration giữa các repository và gọi ngoài
Service nên trả về domain results và lỗi typed — không mã HTTP, không định dạng JSON.
Cái gì nên ở layer data access/repository?
Cô lập các mối quan tâm liên quan đến persistency:
- SQL / query và transaction
- Map rows sang structs
- Trả về lỗi lưu trữ (để service diễn giải)
Tránh mã hóa định dạng phản hồi API hoặc ép buộc quy tắc nghiệp vụ trong repo ngoài các ràng buộc dữ liệu cơ bản.
Validation nên nằm ở đâu?
Một mặc định đơn giản:
- Handlers validate shape của request (thiếu trường, định dạng cơ bản)
- Services validate meaning (quyền, bất biến, trạng thái)
Ví dụ: handler kiểm tra email có tồn tại và trông như email; service kiểm tra email đó được phép và chưa được dùng.
Phản hồi lỗi API tiêu chuẩn nên trông như thế nào?
Dùng một envelope lỗi tiêu chuẩn ở mọi nơi và giữ nó ổn định. Một dạng thực tế gồm:
codecho máy (ổn định)messagecho con người (ngắn và an toàn)detailscho các thông tin có cấu trúc (như lỗi trường)request_idđể truy vết
Điều này tránh các trường hợp đặc biệt phía client và giữ các endpoint sinh tự động dễ đoán.
Làm sao chọn giữa 400 vs 422 vs 409 cho lỗi?
Ghi rõ một bản đồ mã trạng thái và tuân theo nó mỗi lần. Một phân chia phổ biến:
400cho JSON sai định dạng (bad_json)422cho lỗi validation (validation_failed)404cho tài nguyên không tìm thấy (not_found)409cho conflict (duplicate/version mismatch)500cho lỗi bất ngờ
Chìa khóa là nhất quán: không tranh luận từng endpoint.
Có nên bao giờ trả raw database errors cho client không?
Trả về lỗi công khai an toàn và log nguyên nhân thực sự bên trong.
- Phản hồi:
codeổn định,messagengắn, kèmrequest_id - Logs: chi tiết lỗi đầy đủ (SQL, payload upstream, user IDs)
Điều này tránh rò rỉ nội bộ và ngăn các thông điệp lỗi khác nhau xuất hiện lung tung giữa các endpoint.
Endpoint tham chiếu là gì, và tại sao cần nó?
Tạo một endpoint “golden” bạn tin tưởng và yêu cầu các endpoint mới khớp nó:
- Cùng flow (bind → validate → service → error map → JSON)
- Cùng helper (
BindJSON,WriteJSON,WriteError, ...) - Cùng layout thư mục và cách đặt tên
Rồi thêm vài test nhỏ (thậm chí table tests cho mapping lỗi) để khóa pattern.
Làm sao để prompt Claude sinh endpoint mới mà không phá cấu trúc?
Cho mô hình ngữ cảnh và ràng buộc chặt:
- Dán endpoint tham chiếu và quy tắc pattern
- Ghi route, ví dụ JSON request/response, và các trường hợp lỗi
- Liệt kê chính xác file nào mô hình được phép xuất
- Nói rõ không được thay đổi (router, error format, helpers)
Sau khi sinh code, từ chối diff nếu nó “cải tiến” kiến trúc thay vì tuân theo baseline.