Mời bạn thưởng thức Newsletter #98.
Impeccable: Design fluency for AI harnesses
Impeccable là một bộ công cụ giúp nâng cao chất lượng thiết kế giao diện khi bạn làm việc với các trợ lý AI như Claude Code, Cursor, Gemini CLI, Codex CLI, GitHub Copilot và nhiều công cụ khác. Vấn đề mà Impeccable giải quyết rất thực tế: hầu hết lập trình viên không có đủ vốn từ vựng thiết kế để ra lệnh cho AI tạo giao diện đẹp, và các mô hình AI hiện nay đều được huấn luyện trên cùng một tập mẫu nên thường xuyên tạo ra những giao diện na ná nhau với các lỗi quen thuộc như gradient tím, font Inter ở khắp mọi nơi, hay thẻ card lồng nhau.
Bộ công cụ gồm một skill impeccable dạy AI kiến thức thiết kế sâu trên 7 khía cạnh, 18 câu lệnh chuyên biệt như /polish, /audit, /typeset, /overdrive để điều khiển kết quả một cách chính xác, và một thư viện anti-pattern được đặt tên rõ ràng để AI nhận diện và tránh lặp lại. Ngoài ra còn có chế độ Visual Mode với tiện ích mở rộng Chrome chạy 25 phép kiểm tra tất định (không cần LLM) để phát hiện các lỗi thiết kế trực tiếp trên trang web.
Điểm chính:
- Dạy AI kiến thức thiết kế nền tảng trên 7 khía cạnh, nạp vào mỗi lần AI viết mã nguồn
- 18 câu lệnh thiết kế tạo thành ngôn ngữ chung giữa bạn và AI
- Bộ sưu tập anti-pattern có tên gọi cụ thể giúp AI tránh các lỗi phổ biến
- Visual Mode phát hiện lỗi thiết kế tất định qua tiện ích mở rộng Chrome hoặc lệnh
npx impeccable live - Tương thích với nhiều nền tảng AI: Cursor, Claude Code, Gemini CLI, Codex CLI, VS Code Copilot, Antigravity, Kiro, OpenCode
getdesign.md — Bộ sưu tập DESIGN.md cho AI coding agents
getdesign.md là một thư viện mở do VoltAgent duy trì, tập hợp các tệp DESIGN.md lấy cảm hứng từ hệ thống thiết kế của nhiều trang web nổi tiếng như SpaceX, IBM, Lamborghini và nhiều thương hiệu khác. Ý tưởng rất đơn giản: bạn chỉ cần chọn một tệp DESIGN.md, thả vào dự án của mình, và để các AI coding agent như Claude Code, Cursor hay Copilot tự động xây dựng giao diện theo đúng phong cách thiết kế đó.
Tại thời điểm bài viết, bộ sưu tập có 68 tệp DESIGN.md được cập nhật liên tục. Mỗi tệp mô tả các nguyên tắc thiết kế, bảng màu, kiểu chữ và các thành phần đặc trưng của một thương hiệu, giúp AI có đủ ngữ cảnh để sinh ra mã nguồn giao diện nhất quán và có tính thẩm mỹ. Đây là công cụ hữu ích cho lập trình viên muốn tạo nhanh nguyên mẫu (prototype) với phong cách thiết kế rõ ràng mà không cần tự xây dựng design system từ đầu.
Điểm chính:
- Thư viện 68 tệp DESIGN.md lấy cảm hứng từ các thương hiệu nổi tiếng
- Tích hợp trực tiếp với các AI coding agent để sinh mã nguồn giao diện
- Mã nguồn mở tại repo VoltAgent/awesome-design-md trên GitHub
- Cho phép cộng đồng đề xuất thêm tệp DESIGN.md mới qua trang Request
Chọn cấu trúc Java nào? Hãy để “change driver” quyết định
Bài viết giới thiệu một góc nhìn thú vị để lựa chọn cấu trúc Java phù hợp: thay vì dựa vào thói quen hay quy ước, hãy phân tích “change driver” — bất cứ thứ gì mà khi thay đổi sẽ buộc một phần tử trong hệ thống phải thay đổi theo. Tác giả áp dụng góc nhìn này (gọi là IVP lens) để tách biệt giữa coupling thiết yếu (essential coupling — phần không thể tránh khỏi do bản chất cấu trúc) và coupling phát sinh (accidental coupling — phần do cấu trúc ngôn ngữ áp đặt thêm). Câu hỏi cốt lõi luôn là: cấu trúc này có ép ta ghép nối (couple) nhiều hơn mức tình huống thực sự yêu cầu hay không?
Bài viết đi qua hàng loạt cấu trúc phổ biến trong Java: inner class (có tham chiếu ngầm tới outer), static nested class (không có tham chiếu outer), lambda (chỉ capture những gì thân hàm tham chiếu), method reference, anonymous class, record (Java 25), sealed interface kết hợp pattern matching, và cả vấn đề của mutable field cùng checked exception. Mỗi bổ sung lớn của Java từ phiên bản 8 đến nay đều theo một hướng nhất quán: thu hẹp khoảng cách giữa những gì ngôn ngữ bắt ta phải ghép nối và những gì tình huống cấu trúc thực sự cần — tức là giảm accidental coupling.
Điểm chính:
- Change driver: thứ mà khi thay đổi sẽ buộc phần tử hệ thống thay đổi theo
- Phân biệt essential coupling (tất yếu) và accidental coupling (do ngôn ngữ áp đặt)
- Non-static inner class mang tham chiếu ngầm tới outer, gây coupling không cần thiết nếu không dùng
- Lambda và method reference giúp capture chính xác những gì cần thiết
- Mutable field biến mọi chỗ ghi thành change driver cho mọi chỗ đọc
- Checked exception buộc toàn bộ caller trung gian phải khai báo, kể cả khi không xử lý
- Sealed interface + pattern matching exhaustive giúp giới hạn rõ không gian của change driver
- Hướng phát triển của Java từ 8 đến 25 là nhất quán: giảm accidental coupling
Những lệnh Git nên chạy trước khi đọc mã nguồn một codebase mới
Khi tiếp nhận một codebase mới, tác giả không mở mã nguồn ngay mà mở terminal và chạy một vài lệnh Git. Lịch sử commit cho ta bức tranh chẩn đoán về dự án: ai đã xây dựng, vấn đề tập trung ở đâu, đội ngũ đang triển khai tự tin hay đang lần mò quanh “bãi mìn”. Bài viết giới thiệu 5 nhóm lệnh giúp nhanh chóng định hình sức khỏe của dự án chỉ trong vài phút, trước khi đọc bất kỳ dòng mã nào.
Nhóm lệnh đầu tiên là “file nào thay đổi nhiều nhất” — top 20 file có churn cao trong năm qua; file đầu bảng thường là file mà ai cũng sợ đụng vào. Tiếp theo là “ai xây dựng hệ thống này” — xếp hạng contributor theo số commit để phát hiện bus factor (một người chiếm trên 60% là rủi ro lớn nếu họ rời đi). Nhóm thứ ba là “bug tập trung ở đâu” — lọc commit theo từ khóa liên quan đến bug và so sánh với danh sách churn cao để tìm ra mã nguồn rủi ro nhất. Nhóm thứ tư là “dự án đang tăng tốc hay đang chết” — đếm commit theo tháng và quan sát hình dáng đường biểu đồ. Cuối cùng là “đội ngũ có hay phải chữa cháy không” — tần suất revert và hotfix cho biết team có tin tưởng vào quy trình deploy hay không.
Điểm chính:
- Churn cao ở file mà không ai muốn sở hữu là dấu hiệu rõ nhất của codebase drag
- Nếu một người chiếm từ 60% commit trở lên thì đó chính là bus factor của dự án
- File vừa churn cao vừa xuất hiện trong commit bug là mã nguồn rủi ro nhất
- Đồ thị commit theo tháng phản ánh sức khỏe đội ngũ, không chỉ sức khỏe mã nguồn
- Revert và hotfix thường xuyên là bằng chứng cho quy trình deploy yếu
- Cần chú ý: workflow squash-merge sẽ làm sai lệch thống kê authorship
- Chất lượng commit message ảnh hưởng đến khả năng lập bản đồ bug
Chơi cờ vua bằng SQL thuần
Bài viết chứng minh SQL có khả năng biểu đạt vượt xa những gì nhiều người nghĩ bằng một thử nghiệm thú vị: dựng một bàn cờ vua hoạt động được chỉ với SELECT, UPDATE, DELETE và INSERT — không JavaScript, không framework. Bàn cờ 8x8 được biểu diễn dưới dạng một bảng với 3 cột (rank, file, piece), ban đầu chứa 32 dòng tương ứng 32 quân cờ ở vị trí khởi đầu.
Mẹo then chốt là biến các dòng dữ liệu thành lưới 8x8 bằng kỹ thuật pivot: GROUP BY theo rank (hàng), rồi dùng CASE bên trong MAX() để chọn ra quân cờ ở từng file (cột). Di chuyển quân được thực hiện bằng UPDATE, còn bắt quân là DELETE quân bị bắt rồi UPDATE vị trí quân đi. Bài viết còn tái hiện ván cờ nổi tiếng “Opera Game” của Paul Morphy năm 1858 bằng một loạt câu lệnh SQL, kết thúc bằng một nước chiếu bí đẹp mắt. Kỹ thuật pivot này có thể áp dụng cho bất kỳ dạng trực quan hóa dạng lưới nào: lịch, sơ đồ chỗ ngồi, bàn cờ, heatmap.
Điểm chính:
- Bàn cờ được mô hình hóa bằng một bảng 3 cột đơn giản: rank, file, piece
- Pivot từ dòng sang lưới bằng GROUP BY kết hợp CASE trong MAX()
- Di chuyển quân chỉ cần UPDATE, bắt quân dùng DELETE rồi UPDATE
- Kỹ thuật pivot áp dụng được cho lịch, heatmap, sơ đồ chỗ ngồi và nhiều dạng lưới khác
- SQL có khả năng biểu đạt phong phú hơn nhiều người vẫn nghĩ
Những “magic file” của Git — các tệp cấu hình ẩn mà bạn nên biết
Bài viết của Andrew Nesbitt tổng hợp các tệp cấu hình đặc biệt mà Git và hệ sinh thái xung quanh tự động nhận diện khi đặt trong repository. Hiểu rõ các tệp này rất quan trọng, đặc biệt khi bạn xây dựng công cụ làm việc với Git repository. Mỗi tệp là một quy ước có ý nghĩa riêng, giúp Git và các công cụ liên quan thay đổi hành vi một cách tự động mà không cần cấu hình toàn cục.
Các tệp được đề cập bao gồm: .gitignore (danh sách pattern bỏ qua, có chuỗi fallback qua nhiều vị trí), .gitattributes (cấu hình filter, diff/merge driver, chuẩn hóa xuống dòng, nhận diện ngôn ngữ cho GitHub Linguist), .lfsconfig (cấu hình Git LFS đi kèm repo), .gitmodules (quản lý submodule), .mailmap (chuẩn hóa identity tác giả cho git shortlog và git blame), .git-blame-ignore-revs (loại bỏ các commit định dạng lớn khỏi kết quả git blame), và .gitmessage (mẫu commit message). Bài viết cũng đề cập các thư mục đặc thù của từng nền tảng như .github/, .gitlab/, .bitbucket/, cũng như các quy ước rộng hơn như .editorconfig, .ruby-version — cùng pattern: đặt một dotfile vào repo, công cụ tự phát hiện và thay đổi hành vi.
Điểm chính:
.gitignorecó chuỗi fallback qua nhiều vị trí, hỗ trợ wildcard và directory marker.gitattributescấu hình filter, diff/merge driver, và ảnh hưởng đến nhận diện ngôn ngữ của GitHub Linguist.gitmoduleslưu cấu hình submodule, nhưng submodule không quản lý versioning tốt.mailmapgiúp gộp commit từ nhiều email/tên khác nhau của cùng một người.git-blame-ignore-revsgiúpgit blamebỏ qua các commit format code hàng loạt.gitmessagelà mẫu commit message, kích hoạt quagit config commit.template- Các nền tảng (GitHub, GitLab, Bitbucket, Forgejo, Gitea, SourceHut) có thư mục riêng cho CI/CD và template
- Pattern “drop a dotfile, tools detect automatically” mở rộng ra ngoài Git:
.editorconfig,.ruby-version, v.v.
Thêm “điều kiện đúng đắn” vào quy trình thay đổi mã nguồn
Tác giả (Jessitron) chia sẻ một tình huống quen thuộc trong thời đại coding agent: xem một PR do agent tạo ra, thấy nó quên cập nhật tài liệu đi kèm. Thay vì bình luận trên PR đó, tác giả muốn giải quyết vấn đề cho tất cả PR trong tương lai bằng cách tự động hóa. Câu hỏi đặt ra: nên cập nhật chỉ dẫn (instructions) trong AGENTS.md trước, hay nên thêm bước xác thực (validation) tự động trước?
Tác giả lập luận rằng nếu chỉ đổi instructions, mọi thứ sẽ có vẻ hoạt động nhưng không có gì đảm bảo điều kiện đúng đắn — đến một lúc nào đó agent sẽ quên cập nhật tài liệu và ta có khả năng không phát hiện ra. Ngược lại, nếu thêm validation trước, mọi PR đều được kiểm tra; PR thiếu sẽ bị từ chối và agent buộc phải sửa. Khi đã có validation, việc cập nhật instructions chỉ còn là tối ưu hóa để giảm số vòng feedback. Cách tiếp cận này tương tự test-first nhưng ở mức cao hơn: ta không hardcode “PR nào cũng phải cập nhật README” mà phát biểu một thuộc tính — “tài liệu phải luôn cập nhật sau mỗi thay đổi tính năng”. Đây giống property testing hơn là unit testing.
Điểm chính:
- Chỉ sửa instructions không đảm bảo điều kiện đúng đắn vì agent có thể quên và ta không phát hiện được
- Thêm validation tự động trước giúp đảm bảo mọi PR đều được kiểm tra
- Khi đã có validation, việc sửa instructions trở thành một bước tối ưu hóa
- Cách tiếp cận này là test-first ở cấp hệ thống, và mang tính property testing thay vì unit testing
- Boy Scout Rule mới: không chỉ làm cho mã nguồn sạch hơn, mà làm cho cả hệ thống phát triển mạnh hơn
- PR review giờ kiêm luôn system review: cần sửa gì trong ngữ cảnh và feedback của agent?
Tác động của AI lên kỹ sư phần mềm năm 2026 — các xu hướng chính
Bài viết của Pragmatic Engineer tổng hợp kết quả khảo sát từ hơn 900 câu trả lời của các kỹ sư phần mềm và lãnh đạo kỹ thuật về tác động của công cụ AI trong năm 2026. Thay vì so sánh công cụ cụ thể, báo cáo tập trung vào ảnh hưởng tổng quát mà các công cụ này tạo ra cho cộng đồng kỹ sư. Ba chủ đề nổi bật xuyên suốt khảo sát: chi phí AI ngày càng tăng, nhiều kỹ sư chạm ngưỡng giới hạn sử dụng, và tác động không đồng đều giữa các nhóm kỹ sư khác nhau.
Về chi phí, khoảng 15% người trả lời bày tỏ lo ngại. Công ty thường trả các gói “max” của Claude Code, Cursor, Codex ở mức 100-200 USD/tháng/kỹ sư, trong khi một số nơi chỉ có ngân sách 20 USD/tháng (ngang GitHub Copilot). Các doanh nghiệp đang trong giai đoạn thử nghiệm, ngân sách thường được xem là không bền vững về lâu dài. Công ty châu Âu đòi hỏi chứng minh giá trị rõ ràng, trong khi công ty Mỹ sẵn sàng đầu tư trước rồi đo lường sau. Về giới hạn sử dụng, khi bị chạm ngưỡng, lập trình viên thường chuyển sang công cụ khác, dùng API key riêng, hoặc nâng cấp gói cao hơn. Tác giả chia kỹ sư thành 3 nhóm (builder, shipper, và nhóm thứ ba bị ẩn sau paywall): builder dùng AI hiệu quả cho các công việc tẻ nhạt nhưng đòi hỏi kinh nghiệm (cải thiện độ bao phủ test, thay đổi codebase lớn); shipper là nhóm hào hứng nhất với AI vì họ ship nhanh hơn đáng kể.
Điểm chính:
- Khoảng 15% người khảo sát bày tỏ lo ngại về chi phí AI
- Gói “max” của Claude Code, Cursor, Codex thường ở mức 100-200 USD/tháng/kỹ sư
- Doanh nghiệp đang thử nghiệm, nhiều người tin ngân sách hiện tại không bền vững
- Công ty châu Âu đòi hỏi chứng minh giá trị; công ty Mỹ sẵn sàng đầu tư trước
- Chiến thuật tiết kiệm: bắt đầu với Opus ở chế độ plan, rồi chuyển sang Sonnet/Composer để thực thi
- Khi chạm giới hạn, lập trình viên chuyển công cụ, mượn API key, hoặc nâng gói
- Builder tận dụng AI cho công việc tẻ nhạt (test coverage, refactor lớn) đòi hỏi kinh nghiệm
- Shipper là nhóm hào hứng nhất vì AI giúp họ đưa sản phẩm ra production nhanh hơn
- AI khuếch đại xu hướng và thói quen đã có sẵn, tác động không đồng đều giữa các nhóm
Flat error code không đủ dùng cho thư viện IO
Bài viết phản biện lại quan điểm “mỗi thư viện chỉ cần một kiểu lỗi duy nhất với hai thành phần: một thông điệp cho người dùng và một enum ErrorCode phẳng cho máy xử lý.” Tác giả đồng ý rằng cách tiếp cận này hoạt động tốt trong mã ứng dụng của riêng bạn, nơi ít khi cần phục hồi lỗi phức tạp. Nhưng với thư viện cấp cao thiên về I/O — nơi caller cần đủ chi tiết để phục hồi có chủ đích — flat error code không đủ.
Tác giả đưa ví dụ cụ thể từ codebase thực tế: stack sea_orm (ORM) dựng trên sqlx (database driver). sea_orm::DbErr chứa nested sqlx::Error, chứa tiếp sqlx::DatabaseError, và bên trong có thông tin thô từ DBMS như loại vi phạm ràng buộc (unique, foreign key, check) cùng tên ràng buộc. Ứng dụng dùng dữ liệu có cấu trúc này để tạo thông điệp dễ hiểu cho người dùng khi có lỗi validation. Nếu sea_orm làm phẳng hết và chỉ expose error code đơn giản, ứng dụng sẽ phải parse ngược lại từ thông điệp text — một cách kém cỏi. Biết rằng “có vi phạm CHECK constraint” là chưa đủ; cần biết tên constraint để xử lý đúng. Vì vậy, với thư viện I/O phức tạp, lỗi lồng nhau với dữ liệu có cấu trúc là cần thiết.
Điểm chính:
- Flat error code (một enum ErrorCode + thông điệp) đủ cho mã ứng dụng thông thường
- Nhưng không đủ cho thư viện IO-heavy cấp cao cần phục hồi lỗi phức tạp
- Ví dụ: sea_orm lồng sqlx::Error để caller có thể phân biệt unique violation, FK violation, check violation
- Caller cần cả loại lỗi và tên constraint — flat code không thể truyền hết thông tin này
- Nếu phẳng hết, ứng dụng phải parse ngược từ text message — cách kém tin cậy
- Nested error data giúp thư viện hạ tầng expose đủ ngữ cảnh mà không phải sao chép logic
Ai sẽ là senior engineer của năm 2035?
Bài viết đặt ra một câu hỏi quan trọng của ngành phần mềm ngay lúc này: những senior engineer của năm 2035 sẽ đến từ đâu? Hàng loạt đợt sa thải hậu Covid, tuyển dụng chậm lại, cộng thêm việc AI đang hút bớt những task nhỏ và bug fix — vốn là bài tập huấn luyện hoàn hảo cho junior — đang khiến đường ống (pipeline) tạo ra senior engineer bị nghẽn ở nhiều khâu cùng lúc.
Tác giả chỉ ra rằng đánh giá dưới góc độ lợi nhuận-chi phí, cắt giảm junior có vẻ hợp lý trong ngắn hạn. Nhưng điều đó đặt ra một câu hỏi nghiêm túc: nếu AI làm mất những công việc mà junior cần để trưởng thành, và số lượng junior cũng ít đi, thì kỹ năng phán đoán — “scar tissue” từ những lần sai và sửa — sẽ được hình thành ở đâu? Bài viết phác họa ba kịch bản cho năm 2035: (1) Khủng hoảng thiếu senior khi hệ thống critical sập lúc 3 giờ sáng nhưng không còn ai đủ hiểu biết để xử lý; (2) Khoảng giữa biến mất, tạo thành thị trường phân cực giữa người rất giỏi và người rất mới; (3) Kịch bản lạc quan theo kiểu lịch sử lặp lại — AI là tầng trừu tượng mới, tạo ra các điểm vào khác chứ không loại bỏ chúng, giống như JavaScript từng “dân chủ hóa” web dev hay cloud từng trừu tượng hóa hạ tầng. Kết quả phụ thuộc vào quyết định ngay hôm nay về việc tuyển ai, mentor ai, và cho ai cơ hội được thất bại an toàn.
Điểm chính:
- Pipeline tạo senior truyền thống dựa vào task thấp rủi ro, pair programming, mentor và học từ sai lầm
- AI đang hút các task nhỏ và bug fix vốn là bài tập huấn luyện cho junior
- Kinh nghiệm (“scar tissue”) không thể thay thế bằng việc hỏi đáp AI
- Kịch bản 1: Năm 2035 thiếu senior trầm trọng khi hệ thống critical cần người hiểu sâu
- Kịch bản 2: Thị trường phân cực, phần giữa biến mất
- Kịch bản 3: Lịch sử lặp lại — AI là tầng trừu tượng mới, điểm vào mới sẽ xuất hiện
- Quyết định hôm nay (tuyển, mentor, cho cơ hội fail) quyết định thế hệ senior của 2035
Repository pattern trong service viết bằng Go
Bài viết hướng dẫn áp dụng Repository pattern — một design pattern thuộc Domain-Driven Design — vào service viết bằng Go. Tác giả mở đầu bằng tình huống quen thuộc: dự án bắt đầu với một file main.go, rồi dần phình to thành mớ hỗn độn với các lớp phụ thuộc chéo, không còn tách biệt được trách nhiệm, khó test. Repository pattern giúp tránh những vấn đề này ngay từ đầu bằng cách đóng gói phần lưu trữ dữ liệu sau các abstraction (interface) được định nghĩa trong domain model, còn các adapter cụ thể (Postgres, SQLite, DynamoDB) nằm ở nơi khác.
Tác giả trình bày một ví dụ thực tế với cấu trúc dự án theo hướng domain-first (phù hợp triết lý package của Go thay vì nhóm theo loại như models/, services/). Mỗi domain có: file model (định nghĩa struct, validation, error đặc thù), service (chứa business logic, phụ thuộc vào repository qua interface — không quan tâm database là gì), repository (hiện thực interface, trực tiếp tương tác database), và transport layer (HTTP/gRPC/WebSocket — có thể đổi mà không cần sửa business logic). Lợi ích lớn nhất: service hoàn toàn có thể test độc lập bằng dependency injection, và việc đổi database chỉ cần thay repository.
Điểm chính:
- Repository pattern tách biệt domain khỏi persistence, phụ thuộc một chiều
- Cấu trúc dự án theo domain-first phù hợp với Go hơn là nhóm theo loại file
- Service chỉ biết repository qua interface, không quan tâm database là gì
- Dễ test nhờ dependency injection; dễ đổi database mà không sửa business logic
- Đổi transport layer (HTTP/gRPC/WebSocket) cũng trở nên đơn giản nhờ business logic đóng gói trong service
Bitwise flag và bitmask trong Go — pattern cấu hình
Bài viết chia sẻ kinh nghiệm từ một tình huống rất thực: khi tác giả đang thêm toggle boolean thứ tám vào một struct cấu hình thì nhận ra đã đến lúc đổi sang bitmask. Mỗi flag là một lũy thừa của 2 (chiếm đúng một bit), và có thể kết hợp bằng OR, kiểm tra bằng AND, xóa bằng AND NOT, đảo bằng XOR — tất cả chỉ bằng một lệnh CPU, không cần branch, không allocation.
Tuy nhiên tác giả khuyên rõ: đừng dùng bitmask cho CRUD API đơn giản — struct boolean vẫn dễ đọc hơn. Bitmask phù hợp khi mô hình hóa file permission (read/write/execute), tập hợp option của middleware gRPC/HTTP được kiểm tra mỗi request, flag của database query builder (distinct, for_update), hoặc khi wrap thư viện C / syscall đã dùng convention này. Bài viết cũng hướng dẫn dùng pattern iota với dịch bit (1 << iota) để khai báo gọn, cùng cách wrap bitmask trong struct với các method để tránh sai sót (Go không type-safe với bitmask), và cách JSON marshal/unmarshal để lưu trữ compact nhưng config vẫn dễ đọc.
Điểm chính:
- Mỗi flag là một lũy thừa của 2, kết hợp bằng OR, kiểm tra bằng AND, xóa bằng AND NOT, đảo bằng XOR
- Bitmask kiểm tra chỉ cần một lệnh CPU, không branch, không allocation
- Không dùng bitmask cho cấu hình đơn giản — struct boolean vẫn tốt hơn
- Dùng khi mô hình hóa permission, middleware option, query builder flag, wrap C/syscall
- Pattern
iotavới dịch bit giúp khai báo cờ rất gọn - Go không type-safe với bitmask — nên wrap trong struct có method
- Custom JSON marshal/unmarshal giúp lưu compact nhưng config vẫn dễ đọc
Thư viện tốt nhất có khi lại là thư viện làm ít hơn
Tác giả đưa ra quan điểm đi ngược số đông: khi viết một thư viện, rất dễ nhiễm “generalization bug” — cố gắng giải quyết mọi tình huống có thể, khiến thư viện phình to, phức tạp và kém giá trị. Dấu hiệu nhận biết là khi bạn luôn hỏi “nếu developer muốn…” và bắt đầu thêm ceremony (Manager, Service), nhiều dependency ngoài, API phức tạp với nhiều kiểu dữ liệu, yêu cầu calling sequence cụ thể, hay thậm chí quá nhiều tài liệu.
Thư viện đơn giản lại có nhiều lợi ích: dễ hiểu, dễ tin, dễ mô tả, dễ thay thế (yếu tố này quý giá hơn ta tưởng), và dễ bảo trì. Đôi khi thư viện thực sự cần xử lý mọi tình huống — khi đó lời khuyên trong bài không áp dụng. Nhưng thường thì nên tự hỏi: đối tượng “khách hàng” của mình là ai? Tình huống phổ biến nhất là gì? Người dùng có thể làm gì đó khác để xử lý phần còn lại không? Tác giả kết luận bằng một khoảnh khắc giác ngộ từ một đồng nghiệp: “Hay là chúng ta đơn giản… không làm cái đó thì sao?” Đây không phải lười, mà là hiểu rõ hệ thống. Thư viện tốt nhất đôi khi không phải thư viện làm nhiều nhất, mà là thư viện biết lùi ra khỏi đường đi.
Điểm chính:
- “Generalization bug” khiến thư viện cố giải quyết mọi tình huống, dẫn đến phức tạp
- Friction từ API phức tạp, quá nhiều Manager/Service, dependency thừa, tài liệu kém
- Thư viện đơn giản dễ hiểu, dễ tin, dễ thay thế — khả năng thay thế là giá trị lớn
- Một số thư viện thực sự cần xử lý mọi trường hợp; lời khuyên này không áp dụng
- Hỏi: “customer” là ai? Tình huống phổ biến nhất là gì? 80% có thể là điểm bắt đầu hợp lý
- Đôi khi câu trả lời tốt nhất là “hay là chúng ta không làm cái đó?”