Bài học đắt giá từ "Vibe-coding": Tại sao tôi quay lại viết code thủ công?
Sau 7 tháng xây dựng công cụ quản lý Kubernetes bằng AI, tác giả nhận ra kiến trúc hệ thống đã sụp đổ dù tính năng thì đầy đủ. Bài viết này chia sẻ 5 nguyên tắc cốt lõi về việc tại sao AI không thể thay thế con người trong việc thiết kế kiến trúc phần mềm.

Tôi đang quay lại viết code bằng tay
Trong 7 tháng qua, tôi đã dành khoảng 30 cuối tuần để xây dựng k10s — một công cụ dashboard dạng TUI (Terminal User Interface) dành cho Kubernetes, tập trung vào việc quản lý GPU. Toàn bộ dự án được xây dựng dựa trên các buổi "vibe-coding" (lập trình theo cảm hứng với sự hỗ trợ của AI Claude). Mọi thứ diễn ra thần tốc cho đến khi kiến trúc hệ thống sụp đổ hoàn toàn.
Đây là câu chuyện về việc tại sao tôi quyết định lưu trữ toàn bộ codebase và viết lại từ đầu, cũng như những bài học xương máu về việc sử dụng AI trong phát triển phần mềm.
Giao diện GPU Fleet View của k10s
Ảo tưởng về tốc độ
k10s bắt đầu như một bản sao của k9s nhưng được tối ưu cho những người vận hành cụm NVIDIA cluster. Nó hiển thị mức sử dụng GPU, các chỉ số DCGM và xác định các node đang rảnh rỗi đang "đốt tiền". Tôi viết bằng Go với framework Bubble Tea và ban đầu, mọi thứ hoạt động rất tốt.
Những tuần đầu tiên thật kỳ diệu. Tôi chỉ cần gõ lệnh cho Claude: "thêm giao diện xem pods với cập nhật trực tiếp", và bùm, nó hoạt động ngay. Các chế độ xem tài nguyên, lọc namespace, streaming logs, điều hướng bằng bàn phím — tất cả đều được hoàn thiện chỉ trong vài buổi. Tốc độ phát triển của tôi nhanh gấp 10 lần bình thường.
Giao diện xem tài nguyên
Sau đó, tôi muốn thêm tính năng quan trọng nhất: Fleet View — màn hình chuyên dụng hiển thị mức phân bổ GPU, nhiệt độ, công suất tiêu thụ của từng node. Claude cũng tạo ra nó trong một lần duyệt. Nó trông thật đẹp mắt.
Nhưng khi tôi chuyển sang chế độ xem pods, màn hình bị trắng. Dữ liệu không cập nhật. Các tab hiển thị sai số lượng. "God Object" (đối tượng Chúa) đã tự tiêu thụ chính nó.
Bài học 1: AI viết tính năng, không viết kiến trúc
Tôi đã ngồi xuống và đọc file model.go gồm 1690 dòng. Tôi kinh hoàng khi thấy một struct duy nhất chứa mọi thứ: bảng, phân trang, thông tin cluster, logs, trạng thái điều hướng, plugin registry... tất cả đều nằm chen chúc trong một nơi.
Vấn đề cốt lõi là AI không nhìn thấy bức tranh toàn cảnh về sự suy giảm kiến trúc theo thời gian. Mỗi lần prompt chỉ chạm đến một đường dẫn code, dẫn đến việc các logic xử lý bị rải rác và chồng chéo lên nhau.
Giải pháp: Bạn phải tự viết kiến trúc trước. Đừng chỉ viết tài liệu thiết kế mơ hồ. Hãy xác định các interface cụ thể, kiểu message và quy tắc sở hữu. Đặt những quy tắc này vào file CLAUDE.md để AI tuân theo trong mọi lần prompt.
Bài học 2: God Object là sản phẩm mặc định của AI
AI có xu hướng gom tất cả vào một struct duy nhất vì nó đáp ứng prompt nhanh nhất với ít mã nhất. Điều này dẫn đến thảm họa khi xử lý phím tắt. Ví dụ, phím s có nghĩa là "tự động cuộn" trong chế độ logs, nhưng lại là "mở shell" trong chế độ pods. Tất cả logic này bị nhét vào một câu lệnh switch phẳng khổng lồ.
Giải pháp: Thiết lập quy tắc sở hữu trạng thái. Không bao giờ thêm trường vào struct App/Model cho trạng thái cụ thể của view. Mỗi view phải là một struct riêng biệt implement một interface chung.
Bài học 3: Ảo tưởng về tốc độ làm rộng phạm vi dự án
Vibe-coding khiến mọi thứ có vẻ rẻ tiền. "Ồ, tôi có thể thêm view pods trong một buổi? Thêm luôn deployments, services, command palette..." Đột nhiên, tôi đang xây dựng một bản sao của k9s — một công cụ đa năng cho mọi người — thay vì một công cụ chuyên biệt cho GPU cluster như định ban đầu.
Mỗi tính năng mới là một nhánh rẽ thêm vào God Object. Sự phức tạp tích lũy vô hình trong khi chỉ số "tốc độ triển khai" thì vẫn cao.
Giải pháp: Viết một tài liệu tầm nhìn nói rõ bạn KHÔNG xây dựng cho ai. Đặt giới hạn phạm vi trong CLAUDE.md và từ chối các tính năng không phục vụ đối tượng mục tiêu.
Bài học 4: Dữ liệu vị trí là quả bom hẹn giờ
Mọi tài nguyên trong k10s được lấy từ API và làm phẳng ngay lập tức thành mảng chuỗi []string. Danh tính cột hoàn toàn dựa vào vị trí index. Ví dụ, ra[3] là cột Alloc, ra[2] là Compute. Nếu bạn thêm một cột vào giữa, mọi hàm sort, render đều bị sai một cách thầm lặng.
AI tạo ra mẫu này vì đó là đường ngắn nhất để hiển thị bảng, nhưng nó là thảm họa bảo trì.
Giải pháp: Không bao giờ làm phẳng dữ liệu có cấu trúc. Hãy để dữ liệu lưu trữ dưới dạng typed struct (ví dụ: FleetNode, PodInfo) cho đến khi gọi hàm render(). Danh tính cột phải đến từ tên trường struct, không phải index mảng.
Bài học 5: AI không sở hữu các chuyển đổi trạng thái
Trong kiến trúc Bubble Tea, hàm Update() là nơi duy nhất trạng thái được thay đổi. Tuy nhiên, k10s đã vi phạm điều này. Các handler thông báo tạo ra các closure thay đổi các trường của Model từ bên trong một goroutine khác, dẫn đến data race (điều kiện tranh chấp) khi hàm View() đang đọc cùng một trường đó.
Giải pháp: Các tác vụ nền (watchers, API calls) KHÔNG BAO GIỜ thay đổi trạng thái UI trực tiếp. Chúng phải gửi kết quả qua kênh (channel) dưới dạng message đã định kiểu. Chỉ vòng lặp sự kiện chính mới được áp dụng thay đổi trạng thái.
Quay lại với bản năng
Tôi đang viết lại k10s bằng Rust. Không phải vì Rust tốt hơn, mà vì đó là ngôn ngữ tôi có thể kiểm soát tốt. Tôi đã viết đủ nhiều Rust để cảm nhận được khi nào có gì đó sai trước khi tôi có thể diễn đạt nó bằng lời. Bản năng đó là thứ mà vibe-coding không thể thay thế.
Thay đổi lớn nhất khác đơn giản hơn: Tôi tự làm công việc thiết kế, bằng tay, trước khi bất kỳ dòng code nào được viết. Các quyết định kiến trúc mà AI từng làm sai giờ đây được đưa vào văn bản trước khi prompt đầu tiên được gửi đi.
AI là một công cụ mạnh mẽ, nhưng nó không thay thế được kỷ luật kỹ thuật. Hãy để AI viết code, nhưng đừng để nó thiết kế ngôi nhà bạn ở.
Bài viết liên quan

Phần mềm
Intel và AMD vá tổng cộng 70 lỗ hổng bảo mật trong Patch Tuesday tháng 5
13 tháng 5, 2026

Phần mềm
Plugin Checkmarx Jenkins bị xâm phạm trong cuộc tấn công chuỗi cung ứng
11 tháng 5, 2026

Công nghệ
Substrate (YC S24) tuyển dụng Technical Success Manager cho nền tảng AI chuyên xử lý thanh toán y tế
13 tháng 5, 2026
