Dùng quy trình greenfield Claude Code để thiết lập cấu trúc, scripts và một lát dọc đầu tiên bạn có thể chạy, test và cải thiện tuần này qua tuần khác.

Bắt đầu từ một repo trống cho cảm giác tự do, nhưng thường biến thành trạng thái hỗn loạn: nhiều file được sinh ngẫu nhiên, build chỉ hoạt động một nửa, và không có chỗ rõ ràng cho thay đổi tiếp theo. Mục đích của quy trình greenfield Claude Code là tránh sự hỗn loạn tuần đầu tiên đó.
Một vài thất bại lặp lại xuất hiện thường xuyên:
Những quyết định ban đầu rất đau khi phải đảo ngược vì mọi thứ đều chất chồng lên nhau. Cấu trúc rối được củng cố dần. Một build thủ công biến thành mười cách thiết lập khác nhau. Nếu bạn không khoá một lệnh dev đơn giản sớm, bạn không thể biết thay đổi làm hỏng app hay chỉ làm hỏng môi trường.
Khi bài viết này nói “ứng dụng chạy được”, nó có ý nghĩa cụ thể: một lệnh duy nhất khởi động dự án, in ra đầu ra dự đoán được, và báo lỗi rõ ràng khi thiếu thứ gì đó. Bạn nên có thể xóa cài đặt cục bộ, clone repo, chạy lệnh đó và thấy kết quả giống nhau.
Một “vertical slice” (tính năng dọc) là phần chức năng nhỏ nhất đầu-cuối chứng minh ứng dụng của bạn là thật. Không phải mock UI. Không phải chỉ một bảng database. Là một đường mỏng xuyên suốt hệ thống, như một trang có form, một endpoint API lưu dữ liệu, một ghi/đọc database và một kết quả nhìn thấy được trên trang.
Nếu bạn có thể chạy app bằng một lệnh và giao hàng một vertical slice, bạn có một nền tảng để lặp lại mà không đoán mò.
Một lát dọc rõ ràng giữ repo gọn gàng và các prompt có trọng tâm. Đây là lúc quyết định bạn muốn demo cái gì đầu-cuối, không phải bạn hy vọng sản phẩm hoàn chỉnh sẽ trở thành gì.
Chọn câu chuyện người dùng nhỏ nhất chứng minh app hoạt động xuyên suốt. Lát dọc tốt chạm UI, dữ liệu và một hành động thực sự. Ví dụ: “Là người dùng, tôi có thể thêm một task và thấy nó xuất hiện trong danh sách sau khi làm mới.” Nó nhỏ nhưng buộc phải có routing, validation, lưu trữ và một màn hình cơ bản.
Chọn một nền tảng mục tiêu cho tuần 1 và bám vào nó. Nếu bạn bắt đầu web, chỉ làm web. Đừng thêm màn hình mobile “phòng trường hợp.” Ngay cả khi bạn dự định dùng nền tảng như Koder.ai sau này, bạn sẽ có kết quả tốt hơn nếu lát dọc đầu tiên giữ trong một lane (React web, hoặc một API Go, hoặc Flutter).
Xác định “hoàn thành cho tuần 1” bằng ngôn từ đơn giản:
Rồi ghi ra ba không-mục tiêu để bảo vệ phạm vi. Ví dụ: không auth, không hệ thống theme, không job nền.
Khi những quyết định đó được ghi lại, prompt sinh mã có thể nghiêm ngặt: xây dựng chỉ những gì hỗ trợ lát dọc, để mọi thứ khác là TODO.
Trước khi bạn yêu cầu Claude sinh bất cứ thứ gì, khoá một vài mặc định. Chúng có vẻ nhỏ, nhưng ngăn chặn việc “đổi tên mọi thứ sau này”.
Đầu tiên, quyết định hình dạng app. Nếu bạn thực sự cần UI trình duyệt và backend, bắt đầu với hai phần rõ ràng (frontend + API) và một chỗ chia sẻ hợp đồng (API types hoặc một schema đơn giản). Nếu app có thể là server-rendered đơn, giữ thành một codebase để dev cục bộ đơn giản.
Tiếp theo, thống nhất quy tắc cấu hình. Dùng file env cục bộ, không commit file đó, và commit một mẫu thay vào (ví dụ .env.example) với placeholders an toàn và chú thích ngắn. Điều này làm onboarding dễ hơn và giảm rò rỉ secret.
Chọn cổng dev mặc định và giữ ổn định. Cổng xuất hiện trong scripts, docs và thông báo lỗi, nên thay đổi sau này rất phiền. Làm vậy cho tên gọi: thư mục, service, package nên theo một quy ước. Tính nhất quán quan trọng hơn “quy ước hoàn hảo”.
Một bộ quyết định ban đầu đơn giản:
.env cục bộ, .env.example commitVí dụ: bạn chọn web ở cổng 3000 và api ở 8080. Mẫu env của bạn bao gồm API_URL=http://localhost:8080 và DATABASE_URL=.... Khi Claude sinh scripts và docs sau này, mọi thứ sẽ snap vào chỗ thay vì drift.
Bắt đầu bằng việc yêu cầu một scaffold chạy được, không phải “toàn bộ app.” Con đường nhanh nhất dẫn đến output lộn xộn là yêu cầu tính năng trước khi có chỗ đặt chúng.
Hãy rõ ràng về cấu trúc. Yêu cầu một bố cục thư mục với chú thích ngắn giải thích chỗ đặt gì và chỗ không đặt gì. Điều đó buộc phải quyết định ngay từ đầu thay vì để file vương vãi khi sinh ra.
Một cách đơn giản để giữ kỷ luật là đặt quy tắc trong prompt:
Đây là một prompt bạn có thể dùng lại và chỉnh:
\nYou are working in an empty repo. Create a minimal runnable skeleton.\n\nConstraints:\n- Keep it small: no real features yet.\n- Propose a clear folder structure and add brief comments in each folder’s README.\n- Add scripts for: setup, dev, test, build. They must work on a fresh machine.\n- Tell me exactly how to run it, and what output I should see.\n- After generating, stop and wait for my “ran it” confirmation.\n\nOutput:\n1) File tree\n2) Key files (only)\n3) Run instructions\n
Rồi giữ vòng lặp ngắn. Đừng yêu cầu năm thay đổi cùng lúc. Sinh một thay đổi nhỏ, chạy nó, dán lỗi chính xác (hoặc thành công), rồi yêu cầu sửa tối thiểu. Nhịp sinh-chạy-sửa đó giữ dự án dự đoán được và khó để cấu trúc trôi dạt.
Bắt đầu với một lời hứa: ai cũng có thể clone repo và chạy một lệnh để thấy thứ gì đó hoạt động. Điều đó cho bạn một nền tảng ổn định trước khi yêu cầu AI thêm tính năng thực sự.
Tạo repo và viết một README nhỏ khi mọi thứ còn mới. Giữ thực tế: prerequisites, một lệnh dev duy nhất, và cách chạy tests (dù tests hiện tại rỗng).
Tiếp theo, chọn bố cục top-level phù hợp với hình dạng app bạn đã chọn.
Nếu bạn xây nhiều phần deployable (ví dụ frontend + API), một layout workspace có thể giúp:
/
apps/
packages/
scripts/
docs/
README.md
Nếu bạn xây một app đơn, giữ đơn giản và tránh thêm cấp đến khi cần.
Giờ thêm các guardrails tối thiểu để code giữ nhất quán. Chọn một formatter và một linter, chấp nhận mặc định của chúng, và thêm một file config cho mỗi cái. Mục tiêu là diff sạch, không phải rules hoàn hảo ngay ngày đầu.
Làm trải nghiệm dev dự đoán được với một lệnh luôn chạy từ root repo. Đây là một hình dạng đơn giản:
{
"scripts": {
"dev": "echo \"start dev server here\"",
"build": "echo \"build here\"",
"test": "echo \"tests here\"",
"lint": "echo \"lint here\""
}
}
Trước khi sinh thêm thứ gì, chạy lệnh dev đó, xác nhận nó thoát sạch (hoặc khởi động server placeholder), rồi commit đầu tiên chỉ với scaffolding. Nếu đồng đội (hoặc bạn tương lai) có thể tái tạo thiết lập từ đầu, bạn sẵn sàng xây lát dọc đầu tiên.
Một cấu trúc greenfield tốt làm hai việc: giúp bạn tìm code nhanh và làm Claude có ít không gian để phát minh pattern mới mỗi khi bạn yêu cầu thay đổi. Mục tiêu không phải hoàn hảo. Là ổn định.
Nếu bạn làm việc trong một app đơn (hoặc trong thư mục apps/<name>/), một layout nội bộ đơn giản thường ổn:
src/ code app (features, phần chung, entry points)config/ cấu hình không bí mậttests/ tests mức cao đọc như hành vi người dùngscripts/ script hỗ trợ (setup dev, reset db, tasks release)docs/ ghi chú ngắn và checklist bạn thực sự duy trìBên trong src/, tách code feature khỏi code shared theo mô hình thay đổi. Code feature thay đổi thường xuyên và nên ở gần nhau. Code shared nên là phần ít thay đổi và tái sử dụng được.
Một quy tắc thực tế: đặt screen UI, handlers và logic theo feature dưới src/features/<featureName>/.... Đặt logging, API clients, component design system và utilities chung dưới src/shared/.... Nếu một helper chỉ hợp lý cho một feature, giữ nó trong feature đó ngay cả khi trông có vẻ tái sử dụng. Di chuyển nó khi bạn có nhu cầu sử dụng thứ hai thực tế.
Tên thư mục nên mô tả mục đích, không phải công nghệ. “features” và “shared” vẫn có ý nghĩa khi stack của bạn thay đổi. Tránh tên như “misc” hoặc “new.”
Giữ docs/ nhỏ. Starter tốt là docs/checklists.md với vài dòng: cách chạy, cách test, cách thêm thư mục feature mới, và “done” nghĩa là gì.
Một repo trông thật khi bất kỳ ai cũng có thể chạy cùng lệnh và nhận cùng kết quả. Scripts là hàng rào: giảm đoán mò, giữ thay đổi nhỏ và làm rõ khi có thứ hỏng.
Bắt đầu với một tập lệnh nhỏ và giữ chúng nhàm chán. Nếu người mới vào (hoặc bạn quay lại sau hai tuần), họ không nên cần flag đặc biệt hay bước ẩn.
Đây là baseline đơn giản bạn có thể áp dụng cho bất kỳ stack nào:
{
"scripts": {
"dev": "node ./scripts/dev.js",
"build": "node ./scripts/build.js",
"test": "node ./scripts/test.js",
"test:quick": "node ./scripts/test.js --quick",
"test:full": "node ./scripts/test.js --full",
"format": "node ./scripts/format.js",
"lint": "node ./scripts/lint.js",
"smoke": "node ./scripts/smoke.js"
}
}
Hãy để script dev là đường dẫn hạnh phúc. Nó nên khởi động app, in ra nơi nó đang chạy và giữ logs dễ đọc. Nếu server không thể khởi động, thất bại nhanh với một thông báo rõ (thiếu env var, cổng đã dùng, database không reachable).
Script build nên luôn tạo thư mục output sạch. Xoá output cũ trước, rồi tạo artifact mới. Điều đó tránh bug lạ do file cũ để lại.
Với tests, tách quick checks khỏi slow checks. Quick tests chạy trên mỗi thay đổi (unit tests, kiểm tra kiểu). Full tests bao gồm integration và chạy trước merge.
Giữ style nhất quán với một lệnh. Quy tắc đơn giản: format sửa lỗi, lint báo lỗi.
Cuối cùng, thêm một smoke check xác thực các điều cơ bản trước khi bạn mất thời gian gỡ lỗi:
buildLát dọc đầu tiên của bạn nên chứng minh app hoạt động đầu-cuối, không chỉ UI đẹp. Điều đó có nghĩa một tính năng nhỏ chạm màn hình, logic và một dạng lưu trữ, kể cả lưu trữ tạm.
Chọn thứ nhàm chán và hữu dụng, như “Thêm ghi chú” hoặc “Tạo task.” Giữ nó nhỏ đủ để hoàn thành trong một lần ngồi, nhưng đầy đủ để bạn có thể click và thấy trạng thái thực sự thay đổi.
Một lát dọc tốt có bốn phần: một route hoặc screen, một form, một hành động lưu, và một hiển thị. Ví dụ: trang “New Task” với input tiêu đề, nút Save gọi một hàm duy nhất, và một danh sách hiển thị tasks đã lưu.
Bắt đầu với store placeholder để chạy nhanh. Mảng in-memory, file JSON cục bộ hoặc stub interface đơn giản đều được. Chìa khoá là tạo ranh giới mà bạn sẽ thay thế sau này. Nếu code của bạn gọi taskRepository.save(task) hôm nay, chuyển sang database thật sau này là thay đổi nhỏ, không phải viết lại.
Giữ UI đơn giản. Bỏ qua debate hệ thống thiết kế, trạng thái rỗng và animation.
Các kiểm tra chấp nhận bạn có thể làm trong hai phút:
Sau khi có skeleton chạy được và một lát dọc, mục tiêu chuyển sang: làm cho lỗi dễ nhận biết và sửa nhanh. Đây là nơi nhiều khởi đầu greenfield sụp đổ, không phải vì tính năng khó, mà vì thay đổi nhỏ bắt đầu gây bất ngờ.
Đặt một thanh ổn định nhỏ bạn đáp ứng mỗi lần thêm lát dọc:
Ví dụ cụ thể: lát dọc đầu tiên cho phép user tạo một “Project” và thấy nó trong danh sách. Thêm test khởi động server, gọi endpoint tạo, rồi fetch danh sách và kiểm tra item mới xuất hiện. Nếu fail, nó nên fail to rõ ràng với một thông báo hữu ích như “Create Project endpoint returned 500”, không phải biển output.
Về xử lý lỗi, giữ một bộ nhỏ các phản hồi nhất quán. Validation trả về thông báo ngắn (“Tên là bắt buộc”) và tên trường. Lỗi bất ngờ trả về “Có lỗi xảy ra. Thử lại.” Lưu chi tiết vào logs.
Logging hữu ích nhất khi trả lời: request gì, user nào (hoặc anonymous), gì thất bại và ở đâu. Ở dev, bao gồm request id và thời gian, nhưng tránh in token, password, API key hoặc payload đầy đủ mặc định.
Thêm một health check nhỏ. Trên web, có thể là endpoint /health trả ok. Trên mobile, là trạng thái “Connected” chuyển thành “Offline” khi app không reach backend. Đây là tín hiệu nhanh trước khi bạn debug nhầm chỗ.
Cách nhanh nhất lãng phí khởi đầu greenfield là yêu cầu model tạo cả app, rồi chạy nó sau. Sinh lớn ẩn lỗi nhỏ: thiếu dependency, path import sai, scripts giả định công cụ bạn không có. Xử lý mọi output như thứ bạn nên chạy trong vài phút.
Một bẫy khác là thiết kế kiến trúc hoàn hảo trước khi có feature. Tranh luận tên thư mục cảm thấy hữu ích, nhưng không có lát dọc thật bạn không biết chỗ nào bất tiện. Cấu trúc đơn giản hỗ trợ một đường chạy hoạt động tốt hơn cấu trúc thông minh bạn chưa test.
Command drift cũng phổ biến. AI thêm cách khởi động server mới, bạn thêm một cách nữa cho tests, và sớm không ai biết lệnh “chính” là gì. Nếu đồng đội clone repo và hỏi “Chạy sao?”, bạn đã chịu phí rồi.
Những sai lầm gây nhiều sửa lại nhất:
Một ví dụ đơn giản: bạn sinh một app “complete” có login, theming và billing, nhưng lần chạy đầu tiên fail vì thiếu secret key và không có .env.example. Bạn mất giờ sửa setup thay vì học xem feature có hữu ích không.
Hãy trung thực: một lệnh runnable, một feature nhỏ, một mẫu env, rồi mở rộng.
Trước khi thêm “một feature nữa”, đảm bảo dự án dễ nhặt lại ngày mai (hoặc cho người khác). Tốc độ không phải mục tiêu duy nhất. Dự đoán mới là mục tiêu.
Nếu mục nào thất bại, sửa ngay. Thắt chặt scripts và đổi tên rẻ khi repo còn nhỏ.
Khởi đầu greenfield chỉ có ích nếu bạn lặp lại được. Sau khi lát dọc đầu tiên chạy end-to-end, đóng băng các phần tốt thành một mẫu nhỏ: cùng pattern thư mục, cùng tên script và cùng cách kết nối UI, API và dữ liệu.
Đối xử lát dọc đầu tiên như reference implementation. Khi bắt đầu lát dọc #2, sao chép hình dạng chứ không sao chép code. Nếu lát dọc #1 có route, handler, data access layer và test cơ bản, lát dọc #2 nên theo cùng con đường.
Giữ kế hoạch nhẹ. Một trang note đủ cho 2-3 lát dọc tiếp theo: mục tiêu và hành động người dùng cho mỗi lát (một câu), dữ liệu cần, kiểm tra “done” và rủi ro cần thử sớm.
Rồi biến bảo trì thành thói quen. Mỗi tuần, làm một lượt dọn dẹp ngắn: chặt lại scripts, cập nhật README với bước setup mới, và làm mới file env example để onboarding luôn dễ.
Nếu bạn thích vòng lặp xây dựng dựa trên chat, Koder.ai (koder.ai) là một lựa chọn hỗ trợ chế độ planning cộng với snapshot và rollback, và nó có thể xuất source code khi bạn muốn mang dự án đi nơi khác.
Mục tiêu là một quy trình bạn chạy mà không phải suy nghĩ: lên kế hoạch 2-3 lát dọc, xây một lát dọc, ổn định, lặp lại.