Vibe-coding và Stress-coding: Khi nào nên tin tưởng AI và khi nào cần tỉnh táo trong các dự án thực tế?

07 tháng 4, 2026·12 phút đọc

Tác giả phát hiện 87% lỗi trong mã nguồn do AI tạo ra nằm ở các trường hợp ngoại lệ (edge cases) thay vì chức năng chính. Điều này dẫn đến một cái nhìn mới về "vibe-coding": khi nào nên để AI tự do sáng tạo và khi nào cần kiểm soát chặt chẽ trong môi trường sản xuất thực tế.

Vibe-coding và Stress-coding: Khi nào nên tin tưởng AI và khi nào cần tỉnh táo trong các dự án thực tế?

87% lỗi mà tôi tìm thấy trong mã nguồn do AI tạo ra xuất hiện ở các trường hợp ngoại lệ (edge cases) mà prompt không hề đề cập đến. Không phải ở chức năng chính, mà ở những góc khuất.

Khi nhìn thấy con số đó trong lịch sử Pull Requests (PR) của chính mình, tôi đã phải đọc lại hai lần. Bởi vì suốt nhiều tuần qua, tôi vẫn ca ngợi việc Cursor và Claude đã giúp ích tôi thế nào, và kết quả là 87% các lần sửa lỗi của tôi lại diễn ra chính xác ở những nơi mà bối cảnh kinh doanh quan trọng hơn cú pháp.

Điều đó khiến tôi phải suy nghĩ lại về vibe-coding. Và hôm nay, tôi muốn nói thẳng về vấn đề này.

Vibe-coding và năng suất thực tế với AI — Sự khác biệt mà ít người giải thích cho bạn

Hiện đang có một xu hướng (hợp lý và thú vị) cho rằng tương lai của lập trình là vibe-coding: bạn mô tả những gì mình muốn, AI tạo ra mã, bạn điều hướng bằng các prompt, và mã nguồn xuất hiện gần như tự động. Tôi đã xem bài viết lan truyền trên Dev.to gần đây về "stress-coding" — mặt đối lập đầy lo âu — và mặc dù bài đăng đó khá cơ bản, nhưng khái niệm này đã giúp tôi thấu hiểu điều mà tôi đang day dứt.

Vibe-coding không xấu. Nó phụ thuộc vào bối cảnh.

Tôi vẫn vibe-code. Mỗi ngày. Nhưng không phải mọi dự án đều giống nhau. Sự phân biệt mà tôi mất thời gian để diễn đạt là:

  • Khi tôi thử nghiệm (thử nghiệm nhanh): AI là người phụ lái với cương vị được nới lỏng.
  • Khi có người dùng thực: AI là một công cụ mạnh mẽ mà tôi kiểm toán với tiêu chí của riêng mình.

Nghe có vẻ hiển nhiên. Nhưng nó không hề hiển nhiên khi bạn đang trong trạng thái "flow" và mọi thứ có vẻ hoạt động tốt.

Cách tôi sử dụng AI ở chế độ thử nghiệm (và tại sao điều đó ổn)

Khi tôi xây dựng juanchi.dev, quy trình gần như là vibe-coding thuần túy trong vài tuần đầu. Next.js 16, React 19, Tailwind v4 — tất cả đều là công nghệ tiên tiến nhất (bleeding edge), tài liệu còn ít, và tôi dùng AI làm tuyến tham vấn đầu tiên.

Trong bối cảnh đó, quy trình là:

// Prompt điển hình ở chế độ thử nghiệm:
// "Tôi cần một component để tạo hiệu ứng nhập cho các cards
// sử dụng Framer Motion với hook useAnimate mới"

// AI tạo ra cái này, tôi chấp nhận và thử:
import { useAnimate, stagger } from 'framer-motion'

export function AnimatedGrid({ items }: { items: PostCard[] }) {
  const [scope, animate] = useAnimate()

  // AI đề xuất cách tiếp cận này — tôi thử, nó chạy, tôi tiếp tục
  useEffect(() => {
    animate(
      '.card',
      { opacity: [0, 1], y: [20, 0] },
      { delay: stagger(0.1) }
    )
  }, [])

  return (
    <div ref={scope} className="grid gap-6">
      {items.map(item => (
        <div key={item.slug} className="card">
          <PostCard {...item} />
        </div>
      ))}
    </div>
  )
}

Ở chế độ thử nghiệm, nếu cái này thất bại ở một edge case, cái giá phải trả là: tôi nhận ra, sửa lại, và tiếp tục. Không có người dùng đang đợi. Không có SLA. Vibe-coding ở đây nhân đôi tốc độ khám phá của tôi một cách thực tế.

Vấn đề bắt đầu khi tư duy này không thay đổi khi chuyển sang môi trường sản xuất (production).

Cách tôi sử dụng AI khi có liên quan đến sản xuất (stress-coding, nhưng theo hướng tích cực)

Tôi có một dự án khách hàng — thương mại điện tử, lưu lượng truy cập thực, đơn hàng thực. Khi tôi làm việc tối ưu hóa hiệu suất cho ứng dụng đó, quy trình với AI hoàn toàn khác biệt.

Những gì đã thay đổi:

1. Luôn bao gồm bối cảnh kinh doanh trong prompt

// Prompt ở chế độ sản xuất:
// "Tôi có một query lấy đơn hàng trong 30 ngày qua
// với JOIN đến người dùng và sản phẩm. Nó chạy mỗi khi
// ai đó vào dashboard admin. Trung bình 2.3 giây.
// Bảng orders có 180k hàng. Làm sao để tối ưu nó?
// TÔI KHÔNG muốn giải pháp làm vỡ phân trang hiện có."

// Những gì AI tạo ra, tôi KHÔNG chấp nhận mà không xem xét:
const getRecentOrders = async (page: number, limit: number) => {
  // AI đề xuất index phức hợp này — tôi đánh giá trên staging trước
  // CREATE INDEX idx_orders_created_user 
  // ON orders(created_at DESC, user_id) 
  // WHERE created_at > NOW() - INTERVAL '30 days';

  return await db
    .select({
      id: orders.id,
      total: orders.total,
      // Chỉ những trường tôi thực sự cần — AI muốn lấy tất cả
      userName: users.name,
      // Bỏ JOIN products vì không hiển thị ở view này
    })
    .from(orders)
    .innerJoin(users, eq(orders.userId, users.id))
    .where(
      and(
        gte(orders.createdAt, sql`NOW() - INTERVAL '30 days'`),
        eq(orders.status, 'completed') // AI không biết tôi chỉ muốn completed
      )
    )
    .orderBy(desc(orders.createdAt))
    .limit(limit)
    .offset((page - 1) * limit)
}

AI không biết rằng tôi chỉ muốn các đơn hàng completed. Bộ lọc đó đã thay đổi query từ 2.3 giây xuống còn 400ms mà không cần index mới. Bối cảnh kinh doanh mà tôi đóng góp có giá trị hơn nhiều so với mã mà nó tạo ra.

2. Không có gì được đưa lên production nếu tôi không hiểu từng dòng

Nghe có vẻ cơ bản và đúng là vậy. Nhưng ở chế độ vibe-coding, rất dễ để bấm "Chấp nhận tất cả (Accept All)" và tiếp tục. Ở môi trường sản xuất, nếu bạn không thể giải thích một hàm làm gì trong 30 giây, đừng deploy nó.

3. Tôi tự suy nghĩ về các edge cases, không ủy quyền chúng

Quay lại con số 87% ở phần đầu: edge cases chính là nơi bối cảnh kinh doanh quan trọng nhất. Điều gì xảy ra nếu người dùng hủy đơn hàng ngay khi thanh toán đang được xử lý? Điều gì xảy ra nếu hàng tồn kho về 0 giữa addToCartcheckout? Điều đó không có trong prompt. Nó sẽ không bao giờ ở trong prompt trừ khi bạn đưa vào đó.

Những lỗi tôi đã mắc phải khi trộn lẫn hai chế độ

Vấn đề thực sự không phải là vibe-coding hay stress-coding. Vấn đề là không biết bạn đang ở chế độ nào.

Trong hành trình 30 năm với công nghệ của tôi, tôi từng xóa một server production bằng lệnh rm -rf trong tuần đầu tiên làm sysadmin. Đó là stress-coding không có tiêu chí: sự cấp bách, áp lực, thực thi mà không suy nghĩ. Phiên bản hiện đại là vibe-coding trong production: tốc độ, flow, "Accept All" mà không kiểm toán.

Hai lỗi cụ thể tôi đã mắc phải:

Lỗi 1: Tin vào các kiểu TypeScript do AI tạo ra mà không xác thực ở runtime

// AI tạo ra cái này, tôi chấp nhận:
type OrderResponse = {
  id: string
  total: number
  items: OrderItem[]
}

// Vấn đề: API thực thỉ đôi khi trả về total dưới dạng string
// TypeScript không phát hiện ở runtime, nhưng Zod thì có:
import { z } from 'zod'

// Điều tôi lẽ ra nên làm từ đầu:
const OrderResponseSchema = z.object({
  id: z.string(),
  total: z.coerce.number(), // coerce xử lý string -> number
  items: z.array(OrderItemSchema)
})

// Bây giờ nếu API phá vỡ hợp đồng, tôi biết ngay ở runtime
// thay vì khi người dùng thấy "NaN" trong tổng tiền của họ

Lỗi này tốn của tôi 2 giờ gỡ lỗi (debug) ở production. Stack công nghệ tôi chọn ngày nay bao gồm Zod bắt buộc vì lý do này.

Lỗi 2: Để AI quyết định kiến trúc cho các tính năng mới

AI xuất sắc trong việc triển khai (implement). Nó khá kém trong việc thiết kế. Khi bạn hỏi "Làm sao để cấu trúc module thông báo?", nó sẽ đưa ra một câu trả lời đúng về mặt kỹ thuật nhưng vô dụng chung chung cho bối cảnh của bạn.

Các mẫu TypeScript (patterns) mà tôi thực sự sử dụng xuất phát từ các quyết định thiết kế do tôi đưa ra, không phải AI. Nó triển khai các mẫu đó. Tôi quyết định khi nào và tại sao áp dụng chúng.

Những gì đã thay đổi trong quy trình làm việc cụ thể của tôi

Hiện tại tôi có một quy tắc nội bộ đơn giản:

Nếu lỗi production có thể khiến tôi thức dậy lúc 2 giờ sáng, tôi sẽ không vibe-code phần đó.

Cụ thể hơn:

  • Xác thực và ủy quyền: kiểm tra từng dòng.
  • Xử lý thanh toán: zero vibe-coding.
  • Queries cơ sở dữ liệu trong production: tạo bằng AI, nhưng xem xét kế hoạch thực thi.
  • Xử lý lỗi và edge cases: tôi nghĩ ra, AI triển khai.
  • UI components không có trạng thái quan trọng: vibe-coding tự do.
  • Hoạt ảnh, kiểu dáng, bố cục: vibe-coding thả ga.
  • Scripts di chuyển dữ liệu: luôn kiểm tra từng dòng.

Kết quả là tôi dùng AI 80% thời gian viết code, nhưng theo cách phân biệt. Không phải dùng ít AI hơn — mà là dùng AI có tiêu chí.

Câu hỏi thường gặp: Vibe-coding và năng suất thực tế với AI

Vibe-coding chỉ dành cho dự án cá nhân hay có thể dùng trong công việc khách hàng?

Nó có thể dùng trong công việc khách hàng, nhưng ở các lớp cụ thể. Đối với khám phá ban đầu, nguyên mẫu, các component UI không có logic quan trọng — hoàn hảo. Đối với logic nghiệp vụ cốt lõi, tích hợp thanh toán, xử lý dữ liệu nhạy cảm — bạn cần chế độ nghiêm ngặt hơn. Chìa khóa là biết cái nào là cái nào trước khi bắt đầu gõ phím.

Làm sao để tránh chấp nhận code AI có vẻ hoạt động nhưng chứa lỗi ẩn?

Hai thực hành cụ thể: một, luôn chạy tests trước khi commit (bạn có tests chứ nhỉ?). Hai, nếu là code tương tác với external APIs hoặc cơ sở dữ liệu, hãy thử nó với dữ liệu edge case thực: chuỗi rỗng, IDs không tồn tại, phản hồi thiếu trường. AI tạo ra "con đường hạnh phúc" (happy path) tuyệt vời. Bạn phải tự kiểm tra các góc khuất.

Bạn đang dùng công cụ AI nào hiện tại?

Cursor làm editor chính với Claude Sonnet cho công việc hàng ngày. Claude Opus khi cần tư duy kiến trúc hoặc debug cái gì đó phức tạp mà tôi không hiểu. ChatGPT gần như không dùng cho code. GitHub Copilot tôi đã bỏ — Cursor vượt trội hơn về bối cảnh dự án hoàn chỉnh. Bối cảnh là tất cả trong phương trình này.

Vibe-coding có làm bạn mất chiều sâu kỹ thuật theo thời gian không?

Đó là câu hỏi tôi tự hỏi nhiều nhất. Câu trả lời trung thực của tôi: có, nếu bạn không cẩn thận. Cách để chống lại là chủ động chọn hiểu code khó, không chỉ chấp nhận nó. Khi AI tạo ra cái gì đó tôi không hiểu hoàn toàn, tôi nhờ nó giải thích. Không phải vì hoang tưởng — mà để giữ "cơ bắp" hoạt động. Tôi không muốn nền tảng kỹ thuật 30 năm của mình bị teo đi.

Có loại dự án nào bạn KHÔNG dùng AI trong vòng lặp không?

Thành thật mà nói, không. Nhưng có những phần của dự án mà AI có mặt trong vòng lặp theo cách khác. Trong code bảo mật quan trọng, tôi dùng nó để xem xét những gì tôi viết, không phải để tạo. "Đây là triển khai rate limiting của tôi, tôi đang thiếu bảo vệ trước tấn công nào?" Đó là AI như kiểm toán viên, không phải người tạo. Đó là một chế độ khác, không phải vắng mặt AI.

Làm sao bạn biết code do AI tạo ra khi nào tốt và khi nào cần viết lại?

Dấu hiệu cần viết lại: bạn không thể giải thích nó làm gì trong 30 giây, nó có hơn 3 mức lồng ghép (nesting) không rõ lý do, tên biến chung chung (data, result, temp), hoặc không có xử lý lỗi. Dấu hiệu tốt: bạn sẽ tự hào đọc nó trong một code review, các edge cases đã được dự phòng, và nếu có lỗi sai, thông báo lỗi sẽ rõ ràng về cái gì sai và tại sao.

Điều tôi sẽ làm khác nếu bắt đầu ngày hôm nay

Vibe-coding là có thật, là năng suất, và nó ở đây để ở lại. Nhưng câu chuyện "bạn mô tả và AI làm" có một vấn đề: nó bỏ qua việc giá trị của một lập trình viên cấp cao không nằm ở việc viết code. Nó nằm ở việc biết viết code gì, khi nào, và với những đánh đổi (trade-offs) nào.

Tôi bắt đầu dùng AI nghiêm túc trong đại dịch, khi tôi chuyển sang phát triển phần mềm. Những tháng đầu tiên rất tàn khốc — mọi thứ tôi biết về hạ tầng đều vô dụng trong React. Tôi phải học cách suy nghĩ khác. Và hôm nay, với AI, điều tương tự cũng xảy ra: người không học được khi nào tin tưởng và khi nào kiểm toán sẽ gặp phải vấn đề giống như dev sao chép từ Stack Overflow mà không hiểu. Các lỗi sẽ xuất hiện vào thời điểm tồi tệ nhất, ở các góc khuất, đúng nơi doanh nghiệp đau đớn nhất.

AI đã nhân đôi năng suất thực tế của tôi. Nhưng năng suất thực tế bao gồm cả việc không phải thức dậy lúc 2 giờ sáng vì một cái gì đó "có vẻ hoạt động".

Còn bạn, bạn phân chia việc sử dụng AI như thế nào giữa các dự án quan trọng và thử nghiệm? Tôi rất muốn biết tiêu chí của bạn có khác tôi không.

Bài viết này được xuất bản ban đầu trên juanchi.dev

Bài viết được tổng hợp và biên soạn bằng AI từ các nguồn tin tức công nghệ. Nội dung mang tính tham khảo. Xem bài gốc ↗