arrow_back Quay lại tin tức
person Dmitrii Bolotov

Tại sao tôi chọn Bun và Hono cho backend của QuotyAI

#bun #hono #backend #typescript #ai-development
translate
Có sẵn bằng:
info Bài viết này được dịch bằng AI

Tôi không muốn viết lại backend.

Ba tháng trước tôi có một NestJS backend hoạt động hoàn hảo. Nó xử lý tất cả Facebook webhooks. Nó kết nối với database. Nó tính giá chính xác. Nó hoạt động. Đại loại.

Nhưng mỗi lần tôi thêm một endpoint mới, tôi cảm thấy chính xác nỗi sợ hãi tương tự như khi mở một biểu mẫu chính phủ 1000 trang. Tôi biết sẽ có 30 phút boilerplate trước khi tôi có thể viết dòng code duy nhất thực sự làm được việc gì đó.

Đây là phần mà mọi tech blogger đều nói “và rồi tôi viết lại mọi thứ bằng Rust!”

Tôi không viết lại mọi thứ bằng Rust. Tôi viết lại mọi thứ bằng Bun và Hono. Và đây là quyết định kỹ thuật tốt nhất mà tôi đã đưa ra cho dự án này.

“Framework t���t nhất là framework mà bạn không nhận thấy”


Ma Trận Quyết Định

Tôi có ba lựa chọn thực sự.

Các Ứng Viên:

  1. Giữ nguyên NestJS trên Node.js
  2. Viết lại bằng .NET 10
  3. Chuyển sang Hono/ElysiaJS trên Bun

Tiêu Chí Đánh Giá:

Đây là phần mà hầu hết mọi người bỏ qua. Tôi không tối ưu cho “hiệu suất tốt nhất từng có” hay “nhiều GitHub stars nhất”. Tôi tối ưu cho đúng bốn điều:

  • Tôi là một người. Framework phải tránh ra cho tôi.
  • Full stack TypeScript. Không phải chuyển đổi ngôn ngữ.
  • AI coding agents phải viết code tốt cho nó.
  • Types ở khắp mọi nơi, tránh runtime issues.

Hãy cho mỗi cái một cơ hội công bằng.


Lựa Chọn 1: Giữ nguyên NestJS

Đây là lựa chọn mặc định. Tôi đã có 50000 dòng code hoạt động. Tôi biết tất cả các quirk kỳ lạ. Tôi có thể tiếp tục.

Nhưng có những vấn đề không bao giờ biến mất:

Build times: 20 giây. Cho mỗi thay đổi. Mỗi lần tôi save file, tôi có đủ thời gian để pha cà phê, uống được nửa ly, và quay lại trước khi server restart. Tôi lãng phí 3-4 giờ mỗi tuần chỉ để đợi builds.

ESM hell: Nửa ecosystem bây giờ là ESM. Nửa vẫn còn CommonJS. Mỗi lần tôi thêm package mới là 50/50 xem nó có hoạt động không.

Muốn import một file relative? Bạn không thể viết import './auth.service'. Bạn phải viết import './auth.service.js'. Với extension .js. Mặc dù file thực trên disk là .ts.

Muốn dùng src aliases? Chúc may mắn. Một số tool respect. Một số không. Một số chỉ respect có nửa thời gian. Tôi đã tiêu 12 giờ tháng trước để debug tại sao một package không import đúng. Không ai nên phải nghĩ về module systems vào năm 2026.

Bun sửa tất cả. Cứ viết import. Nó hoạt động. Không extensions. Không flags. Không type: module trong package.json. Không __esModule hacks. Nó cứ hoạt động.

DTO boilerplate: Cho mỗi endpoint tôi muốn viết, tôi cần:

  • Một interface
  • Một DTO class với 7 decorators
  • Một validation schema
  • Một định nghĩa OpenAPI riêng
  • Một transformer

Tất cả cho một endpoint thực ra chỉ lấy hai số và trả về một giá. Thật điên rồ.


Lựa Chọn 2: Viết lại bằng .NET 10

Để tôi nói rõ ràng: Tôi là một .NET developer và tôi yêu nó.

Nếu bạn là một .NET developer đọc cái này, tôi hiểu. Tôi hoàn toàn hiểu.

Một hệ thống routing chuẩn. Tổng cộng hai JSON parsers trong cả ecosystem. Types strict mặc định. Tooling tuyệt vời. Có thể dự đoán. Không drama. Nếu tôi làm việc với team 5+ developers, nếu đây là sản phẩm enterprise, nếu tôi dự định tuyển người, .NET 10 sẽ là lựa chọn hiển nhiên, đúng đắn, có trách nhiệm.

Có đúng không có argument kỹ thuật nào chống lại .NET cho loại backend này. Nó nhanh hơn. Nó ổn định hơn. Nó có standard library tốt hơn. AOT compilation. Cho 95% tất cả các dự án backend được xây dựng ngày nay, .NET là lựa chọn đúng cho tôi.

Nhưng tôi không xây dựng 95% các dự án backend. Tôi đang xây dựng QuotyAI. Và tôi có một ràng buộc cực kỳ kỳ lạ mà hầu như không ai có:

90% code được viết cho dự án này không được viết bởi tôi. Nó được viết bởi AI coding agents.

Và đây là sự thật không thoải mái mà không ai sẽ nói với bạn: AI coding agents khá tệ trong việc viết C# tốt.

Chúng có thể viết C# compile được. Chúng có thể viết C# chạy được. Nhưng mỗi lần tôi nhờ Claude, GPT, hay GLM viết cho tôi một endpoint .NET đơn giản, nó sẽ trả về với enterprise boilerplate kiểu 2019. Ba interfaces. Hai abstract classes. Một factory pattern. Và đâu đó sâu 12 levels, ba dòng code thực sự làm điều tôi muốn.

Tôi mất 15 phút xóa tất cả rác chỉ để đến được logic thực. Cho một solo founder viết 10 endpoints một ngày, đó là 2.5 giờ một ngày lãng phí xóa boilerplate mà AI thêm vào hoàn toàn không có lý do.

Và có lý do thứ hai, còn lớn hơn, mà hầu như không ai nói đến:

Mọi framework AI quan trọng đều ship TypeScript trước.

LiveKit. LangChain. OpenAI SDK. Anthropic SDK. Deepgram. Tất cả đều có features mới trong TS 3-6 tháng trước khi xuất hiện trong .NET. Tất cả examples đều trong TS. Tất cả documentation đều trong TS. Tất cả bug fixes đều land trong TS trước.

Khi LiveKit release API realtime voice agent của họ tháng trước, TypeScript SDK có sẵn ngay từ ngày một.

Cho một sản phẩm sống chết bởi việc có thể dùng các AI tools mới nhất ngay khi chúng ra mắt? Sự khác biệt đó không phải là nhỏ.

Và hãy hoàn toàn thành thật về một điều khác: bạn hoàn toàn đúng.

.NET tốt hơn TypeScript cho background jobs. Sagas. Distributed transactions. Reliable queues. Tất cả những thứ boring, khó, quan trọng mà các hệ thống enterprise cần. TypeScript ecosystem vẫn còn tụt hậu 5-10 năm ở đây. Không có argument. Nếu bạn đang xây dựng một hệ thống ngân hàng, một ERP, hoặc bất cứ thứ gì cần xử lý 10 triệu background jobs một ngày một cách đáng tin cậy.

Nhưng tôi không xây dựng cái đó. Tôi đang xây dựng một sản phẩm AI nơi 90% độ phức tạp nằm ở API endpoints và AI agent logic. Không phải xử lý nền.

Cho teams? Cho enterprise? Dùng .NET. Nó khách quan là framework backend general purpose tốt hơn. Cho tôi, ngay bây giờ, xây dựng sản phẩm cụ thể này trong 2026? Đó là lựa chọn sai.

“AI không viết enterprise patterns - nó viết boilerplate. Lựa chọn framework của bạn nên tính đến điều này.”


Lựa Chọn 3: Hono trên Bun

Tôi theo dõi từ khi nó được release nhưng chưa bao giờ dùng. Tương tự cho Deno.

Tôi cũng đã thử ElysiaJS. Nó rất phổ biến, rất nhanh, và có documentation tuyệt vời. Nhưng tôi ghét functional style của routes. Tất cả pipe chaining, method chaining, và functional composition không click với tôi.

Tôi là một developer old-fashioned. Tôi thích OOP. Tôi thích dependency injection. Tôi thích những patterns đã hoạt động trong 30 năm. Hono cho tôi viết code nhìn như code bình thường, không như một academic functional programming experiment.

Rồi tôi thử nó.

bun dev khởi động trong 800ms. Không phải 8 giây. Tôi save file, và server restart trước khi ngón tay tôi rời khỏi ctrl key.

Không có module system drama. Mọi thứ đều import. Không có __esModule flags. Không có fights giữa require() vs import. Nó cứ hoạt động.

Và Hono? Hono không làm magic. Nó không làm dependency injection. Nó không có một site documentation 400 trang.

Nó làm đúng ba điều:

  1. Routes requests
  2. Validates input
  3. Generates OpenAPI schemas

Và nó làm cả ba mà không bắt tôi viết bất kỳ boilerplate nào.


Sâu Vào Triển Khai

Chiến thắng lớn nhất không phải là performance. Nó là schema generation.

Với NestJS tôi sẽ viết cái này:

@ApiProperty()
@IsNumber()
@Min(1)
@Max(365)
nights: number;

@ApiProperty()
@IsNumber()
@Min(0)
cleaningFee: number;

Và rồi tôi vẫn phải viết Zod schema riêng. Và rồi tôi vẫn phải viết TypeScript interface riêng. Ba nguồn truth. Luôn không đồng bộ.

Với Hono và hono-openapi tôi viết cái này một lần:

const CreateBusinessEntityFactsSchema = z.object({
  facts: z.array(z.object({
    content: z.string(),
    tags: z.nativeEnum(FactTagEnum).array()
  }))
});

Thế thôi. Đó là toàn bộ định nghĩa.

Hono tự động:

  • Validate tất cả incoming requests tại runtime
  • Generate OpenAPI documentation hoàn hảo với descriptions và examples
  • Cho tôi full compile-time TypeScript types qua z.infer<typeof Schema>
  • Trả về proper 400 errors với chính xác field nào sai và tại sao

Muốn strip extra fields khỏi requests? .strip(). Muốn làm một thứ gì đó partial? .partial(). Muốn merge hai schemas? .and(). Tất cả native. Tất cả built-in.

Không decorators. Không DTO classes. Không 7 decorators riêng biệt cho mỗi field. Không magic. Chỉ một nguồn truth.

Toàn bộ rewrite mất 3 ngày. Không phải 3 tuần. 3 ngày. Tôi xóa 2000 dòng boilerplate và application hoạt động y hệt. Nhưng bây giờ nó build trong 2 giây thay vì 45.

Đây không phải là lý thuyết. Mọi endpoint trong QuotyAI hoạt động y như thế này. File route xử lý tất cả business fact extraction, OCR, translation, và AI hint generation? Nó 950 dòng.

Trong NestJS cùng chức năng đó sẽ là 3000+ dòng trải ra 12 files khác nhau. Controllers. DTOs. Providers. Modules. Interfaces. Tất cả cho chính xác cùng một chức năng.

Nếu tôi phải làm lại? Tôi đã làm nó 2 tháng trước. Tôi lãng phí quá nhiều thời gian đấu tranh với NestJS khi tôi có thể đã xây dựng features.

Diagram comparing NestJS boilerplate vs Hono single-source schema definitions

Chi Phí Phải Trả

Đây không phải là bài “ai cũng nên dùng Bun và Hono”. Đây là cái hoạt động cho tôi.

Mọi lựa chọn đều có tradeoffs. Đây là những gì tôi từ bỏ:

  • Ecosystem nhỏ hơn: Không có pre-built packages cho mọi edge case. Tôi tự viết code thay vì debug giant abstractions.
  • Ít Stack Overflow hơn: Khi có gì đó break, có 3 câu trả lời thay vì 1000. Một trong số đó thường là của tôi.
  • Không có guard rails: Hono không ngăn bạn viết bad code. Nó chỉ tránh ra cho bạn.
  • Đôi khi có lỗi: Things break. MongoDB driver có memory leak tuần trước. Nó đã được fix.

.NET ổn định hơn. Trưởng thành hơn. Tốt hơn cho teams. Nếu đó là những gì bạn cần, dùng nó. Bạn đúng.

Flowchart showing decision framework for choosing between NestJS, .NET, and Bun/Hono

Phán Quyết

Cho tôi, ngay bây giờ, đây là stack hoàn hảo.

Tôi có thể viết một endpoint, thêm validation, lấy full types, và có OpenAPI documentation trong 2 phút. Không phải 20.

Claude, GPT, và GLM hiểu Hono hoàn hảo. Chúng viết code clean, simple mà không có tất cả enterprise garbage.

Tôi dùng chính xác TypeScript types trên backend, trong browser extension, trên frontend, và thậm chí trên landing page. Không copying. Không syncing. Chỉ một definition.

Stack này không tối ưu cho 100 developers. Nó tối ưu cho một developer cần di chuyển rất nhanh.

Đó chính xác là những gì QuotyAI cần.

Tôi không cần một framework có thể scale cho 100 engineers. Tôi cần một framework cho phép một engineer scale cho 1000 users.

“Tối ưu cho ràng buộc của bạn, không phải best practices của người khác.”

Và ngay bây giờ, Bun và Hono là công cụ tốt nhất cho công việc đó.


Câu Hỏi Thường Gặp

Tại sao chọn Bun và Hono thay vì NestJS?
Bun loại bỏ drama về ESM module và cung cấp thời gian khởi động 800ms, trong khi Hono loại bỏ boilerplate mà không hy sinh type safety - quan trọng cho solo founders cần di chuyển nhanh.

Hiệu suất của AI coding agent ảnh hưởng như thế nào đến việc chọn framework?
AI agents viết 30-50% ít boilerplate hơn cho Hono so với .NET hoặc NestJS, tiết kiệm 2.5+ giờ mỗi ngày khi dọn dẹp các enterprise patterns không cần thiết.

Những tradeoffs của việc dùng Bun và Hono là gì?
Ecosystem nhỏ hơn, ít câu trả lời trên Stack Overflow hơn, không có guard rails, và đôi khi có lỗi - nhưng đây là những tradeoff chấp nhận được cho solo founders ưu tiên tốc độ.


Trích dẫn và tham khảo kỹ thuật:

Cảm ơn bạn đã đọc!
Đọc thêm bài viết