Nghiên cứu tình huống 4: Một trang nội dung không cần bạn chăm sóc
Ý tưởng
Một người có sở thích viết muốn biến một thư mục ghi chú Markdown — các bài đánh giá nhà hàng từ nhiều năm du lịch — thành một trang công khai có thể tìm kiếm được, không cần đăng nhập vào một CMS nào và không có hóa đơn hàng tháng nào. Thêm một ghi chú, đẩy (push) lên, xong.
Bản đặc tả
A static site generated from a folder of Markdown files, one file
per review (title, city, rating, body in frontmatter). Build a
homepage that lists reviews newest-first, a page per review, and
client-side search by city or name. No database, no login, no
build step I have to run by hand. Pushing a new .md file should
publish it.
Yêu cầu ẩn giấu nằm ở câu cuối cùng. "No build step I have to run by hand" (Không có bước build nào tôi phải tự tay chạy) có nghĩa là bản đặc tả này thực ra nói về quy trình làm việc (workflow), không chỉ về kết quả — và những yêu cầu về quy trình làm việc là những thứ mà các AI sẽ bỏ qua trừ khi bạn nêu tên chúng ra, bởi vì chúng không hiện diện trong ứng dụng đang chạy.
Stack công nghệ
Một trình sinh trang tĩnh (static site generator) đọc Markdown tại thời điểm build, triển khai lên một máy chủ CDN có build được kích hoạt bởi . Đẩy lên kho mã, máy chủ build lại, CDN phục vụ nó. Tìm kiếm chạy phía trình duyệt (client-side) trên một chỉ mục nhỏ được sinh ra khi build, nên không cần máy chủ và không tốn chi phí truy vấn — một sự đánh đổi hợp lý ở quy mô vài trăm bài đánh giá, và là một sự đánh đổi mà chúng tôi chủ động chọn chứ không phải tình cờ phát hiện ra.
Toàn bộ quy trình làm việc chỉ gói gọn trong một mũi tên: một lần đẩy lên kích hoạt một lần build, việc build đó xuất bản trang. Không có gì chạy vào lúc có yêu cầu (request) ngoại trừ chính trình duyệt của khách truy cập lọc qua một chỉ mục:
add review.md ┌──────────────────┐
git push ───────────▶ │ CDN HOST │
│ build step: │
│ read all *.md │──▶ static HTML pages
│ emit search.json│──▶ tiny search index
└────────┬─────────┘
│ serves
▼
┌──────────────────┐
│ VISITOR BROWSER │ filters search.json
│ (client-side) │ locally — no server
└──────────────────┘
no database · no login · no build step run by hand
Những câu lệnh then chốt
Chúng tôi neo AI vào hình dạng dữ liệu (data shape) trước tiên, trước bất kỳ giao diện nào:
Set up a static site that reads every .md file in /reviews. Each
file's frontmatter has title, city, rating (1-5), date. Parse all
of them at build time into a sorted list (newest first) and
generate: a homepage listing them, and one page per review at
/reviews/<slug>. Show me the data-loading code first, before any
styling.
"Before any styling" (Trước bất kỳ việc tạo kiểu nào) là một đòn bẩy có chủ đích. Yêu cầu toàn bộ mọi thứ cùng một lúc, bạn sẽ có được một trang đẹp đẽ nhưng được nối vào dữ liệu mà bạn không thể tin tưởng; yêu cầu lớp dữ liệu trước, bạn có thể xác minh được nền móng trước khi một điểm ảnh (pixel) nào làm bạn xao nhãng. Sau đó là tìm kiếm, được giới hạn phạm vi để giữ chi phí thấp:
Generate a search.json at build time with {title, city, slug} for
every review. On the homepage, add a search box that filters the
visible list client-side by matching city or title — no network
calls, just filter the already-loaded index. Keep it under 50
lines of JS.
Trở ngại
Quá trình build thành công ở máy cục bộ, nhưng trang đã triển khai lại báo lỗi 404 trên từng trang đánh giá riêng lẻ. Trang chủ hoạt động; các trang từng bài đánh giá thì không. Chúng tôi dán triệu chứng và cấu hình vào thay vì đưa ra giả thuyết:
Homepage works live, but /reviews/<slug> pages all 404 in
production while working in local dev. Here's my build output
directory listing and my host's deploy config: [pasted]. Why does
local dev serve these pages but production doesn't?
The AI traced it to the difference between dev-server routing (which
resolves paths dynamically) and static hosting (which serves only
files that physically exist). The build was generating the review
pages into the wrong output folder, so they never got uploaded —
dev had hidden the bug because its router faked the routes that
production couldn't.
Chúng tôi trỏ thư mục đầu ra (output directory) đến đúng nơi máy chủ thực sự triển khai, các trang xuất hiện, và chúng tôi thêm một dòng vào danh sách kiểm tra trước khi ra mắt: nhấp vào một liên kết sâu (deep link) trên trang thực tế, chứ không chỉ trang chủ. Bài học rộng hơn là "chạy được trong môi trường phát triển (dev)" và "chạy được sau khi triển khai" là hai tuyên bố khác nhau, và xuất bản tĩnh (static export) chính là nơi hai điều đó tách biệt nhau — các máy chủ dev thường dễ dãi với các đường dẫn (route) theo cách mà việc lưu trữ dựa trên tệp thực tế không bao giờ dễ dãi như vậy.
Ra mắt
Chúng tôi đẩy lên một bài đánh giá thật, xem máy chủ build lại, và tải một liên kết sâu đến chính bài đánh giá đó trên tên miền thực tế — đúng trường hợp mà môi trường dev đã "nói dối". Sau đó chúng tôi tìm kiếm theo tên thành phố của nó để xác nhận chỉ mục đã bắt được nó. Việc thêm bài đánh giá tiếp theo chỉ là một một dòng, đó chính là toàn bộ điểm mấu chốt: việc ra mắt không phải là một khoảnh khắc, mà là một quy trình làm việc tiếp tục hoạt động mà không cần chúng tôi.