Mời bạn thưởng thức Newsletter #115.
Go Experiments Explained
Alex Edwards giải thích cơ chế thử nghiệm mà Go thường đưa vào mỗi bản phát hành: có khi là gói mới trong thư viện chuẩn như testing/synctest, có khi là thay đổi trình biên dịch hoặc runtime như bộ gom rác mới, và hiếm hơn là thay đổi hành vi ngôn ngữ như phạm vi biến vòng lặp trước khi thành mặc định ở Go 1.22. Mục đích là lấy phản hồi thực tế trước khi tính năng được chính thức hóa; nếu gây lỗi hoặc bị phản đối, nó có thể được sửa hoặc bỏ hẳn. Vòng đời phổ biến là ban đầu tắt mặc định, sau một hai bản phát hành thì bật mặc định, đôi khi kèm giai đoạn chuyển tiếp cho phép tạm tắt. Nhưng cũng có ngoại lệ: Arenas bị treo vô thời hạn sau phản hồi tiêu cực, còn Swiss tables đi thẳng lên mặc định.
Vì chưa có trang chính thức nào theo dõi trạng thái, tác giả tự tổng hợp bảng danh sách cho Go 1.26 từ go doc goexperiment.Flags, mã nguồn internal/buildcfg và ghi chú phát hành. Cách dùng: liệt kê tên viết thường, phân tách bằng dấu phẩy trong biến môi trường GOEXPERIMENT để bật, thêm tiền tố no để tắt, áp dụng cho go build, go run và go test. Với lập trình viên Go thông thường, các mục đáng quan tâm là GreenTeaGC và Dwarf5 (đã bật sẵn, nên biết cách tắt khi gặp sự cố), JSONv2, GoroutineLeakProfile để dò rò rỉ goroutine, RuntimeSecret cho mã xử lý dữ liệu nhạy cảm và RuntimeFreegc. Lưu ý quan trọng: thử nghiệm không nằm trong cam kết tương thích của Go, nên hãy dùng để thử, đo đạc và góp phản hồi thay vì phụ thuộc quá sớm.
Low-Level Network Optimizations: Socket Options That Matter
Bài viết thuộc Go Optimization Guide phân tích các tùy chọn socket ảnh hưởng trực tiếp tới độ trễ và thông lượng của ứng dụng mạng viết bằng Go. Giá trị mặc định của hệ điều hành ưu tiên an toàn và tương thích, nên dưới tải lớn chúng thường thành nút thắt trước cả CPU hay bộ nhớ. TCP_NODELAY tắt thuật toán Nagle để gói tin nhỏ được gửi ngay, đổi lại tốn băng thông hơn. SO_REUSEPORT cho phép nhiều tiến trình hoặc luồng cùng gắn vào một cổng, nhân hệ điều hành chia kết nối mới cho từng hàng đợi riêng, giảm tranh chấp và tận dụng nhiều nhân CPU. Kích thước bộ đệm SO_RCVBUF và SO_SNDBUF nên xấp xỉ tích của băng thông và thời gian khứ hồi: quá nhỏ thì tăng số lời gọi hệ thống và giới hạn thông lượng, quá lớn thì phí bộ nhớ và làm tăng độ trễ.
Với TCP keepalive, mặc định rất bảo thủ; các giá trị thường gặp trong vận hành là chờ 30–60 giây, cách nhau 10–15 giây, thăm dò 3–5 lần, nhưng chỉnh quá gắt sẽ tăng lưu lượng và dễ báo sai trên đường truyền nghẽn. Trong Go, SetKeepAlivePeriod chỉ điều khiển thời gian chờ ban đầu; muốn chỉnh TCP_KEEPINTVL và TCP_KEEPCNT phải dùng lời gọi hệ thống trực tiếp. SOMAXCONN quyết định số kết nối được xếp hàng chờ, là cấu hình cấp hệ thống qua sysctl. Để đặt tùy chọn đúng thời điểm, tức trước khi socket được gắn cổng, nên dùng hàm Control của net.ListenConfig cùng syscall.RawConn. Thông điệp xuyên suốt: hãy thay đổi từng thứ một, kiểm thử dưới tải thực tế và dựa vào số liệu đo được thay vì sao chép cấu hình từ nơi khác.
When Is 100% Vibe Coding OK ?
Viorel PETCU mở đầu bằng trải nghiệm mất nhiều thời gian xem xét những bản đặc tả được viết “theo cảm hứng”: đọc thì có vẻ mạch lạc nhưng gần như không nói gì cụ thể và không áp dụng được vào hệ thống hiện có. Tác giả không phản đối vibe coding, thậm chí dùng thường xuyên cho script dùng một lần, biểu đồ tạm, bộ phân tích cú pháp tạm hay tài liệu không ai phải bảo trì, vì ở đó tốc độ quan trọng hơn sự chặt chẽ. Nhưng khi phần mềm cần được bảo trì lâu dài, bài toán thay đổi hoàn toàn.
Ví dụ minh họa là một câu đố gỗ gồm 25 mảnh pentacube giống hệt nhau cần lấp đầy khối 5×5×5. Sau khi tự lắp thất bại, tác giả dùng Go và ChatGPT để giải: khó khăn ban đầu không nằm ở thuật toán mà ở cách biểu diễn, từ quy ước tọa độ, ký hiệu lát cắt đến cách xoay và đặt tên mảnh. Khi mô hình đã ổn định, phần còn lại là vét cạn kinh điển với đệ quy và quay lui; phần cứng hiện đại đủ nhanh để không cần tối ưu thêm. Chỉ trong khoảng hai giờ, dự án thành bộ giải tổng quát, có xuất tệp OBJ/MTL để xem trong Blender. Điều cốt lõi là tác giả vượt qua được các giả định sai, ràng buộc bịa đặt và mã lỗi vì đã biết chắc bài toán có nghiệm. Kết luận: vibe coding “ổn 100%” khi con người nắm các bất biến, ràng buộc là thật và đầu ra kiểm chứng được; còn nếu chính yêu cầu cũng chỉ là cảm hứng thì hệ thống không có gì chắc chắn để hội tụ.
Self-calling executables
Olivier giới thiệu kỹ thuật “self-calling executable”: một chương trình đang chạy khởi động một bản khác của chính nó, trực tiếp hoặc gián tiếp, để đảm nhận vai trò khác. Ứng dụng đầu tiên là kiểm thử hàm main như một hộp đen khi mã nguồn phụ thuộc trạng thái toàn cục khiến các bài kiểm thử can thiệp lẫn nhau. Bài kiểm thử gọi lại chính tệp thực thi kiểm thử qua os.Args[0] kèm biến môi trường như TEST_MAIN; trong TestMain, nếu thấy biến này thì chạy lệnh chính thay vì bộ kiểm thử, nhờ đó mỗi lần chạy được cô lập trong một tiến trình con.
Ứng dụng thứ hai là tương tác với công cụ bên ngoài, ví dụ jjui, giao diện dòng lệnh cho Jujutsu. Lệnh ssh dùng biến SSH_ASKPASS để gọi một chương trình khác khi cần hỏi mật khẩu; jjui trỏ biến này về chính nó, nên khi ssh cần mật khẩu, tiến trình cháu kết nối ngược về tiến trình chính qua Unix socket (đường dẫn chứa PID để nhiều phiên chạy song song), chuyển lời nhắc, nhận mật khẩu từ người dùng rồi in ra stdout cho ssh. Vì đây là thao tác nhạy cảm, tác giả thêm hai lớp bảo vệ: mỗi tiến trình con nhận một khóa ngẫu nhiên phải gửi kèm khi kết nối, và tiến trình chính dò chuỗi PID tổ tiên của bên kết nối để xác nhận. Nhược điểm gồm khó gỡ lỗi, nguy cơ đệ quy vô hạn nếu biến môi trường bị truyền sai, và khi bật race detector mỗi lần thoát sẽ ngủ 1 giây, từng khiến kiểm thử tích hợp của Forgejo kéo dài hơn 2 giờ thay vì 45 phút; có thể khắc phục bằng GORACE="atexit_sleep_ms=0".
Modern Engineering Values
Christoph Nakazawa kể rằng giờ ông hiếm khi tự tay viết mã: các dự án như Vite+, fate hay Athena Crisis phần lớn do coding agent viết, chủ yếu qua Codex CLI. Cách làm của ông là yêu cầu agent đề xuất kế hoạch và đặt câu hỏi trước khi thực thi, buộc tạo bài kiểm thử thất bại trước khi sửa lỗi, chạy vài vòng rà soát tự động rồi mới tự xem lại thay đổi. Khi viết mã không còn là nút thắt, việc thảo luận và xem xét mã mới là giới hạn.
Từ đó, bài viết đề xuất những giá trị kỹ thuật cho thời agent. Quyền sở hữu mạnh: agent khuếch đại người hiểu sâu sản phẩm nhưng cũng khuếch đại nhiễu từ người thiếu ngữ cảnh, nên đội hiệu quả sẽ nhỏ, khoảng hai đến ba người, ranh giới rõ, và việc xem xét mã tập trung vào sự thống nhất thay vì tranh cãi từng dòng. Gu tốt để biết điều gì thực sự đáng làm. Rào chắn chặt và vòng phản hồi nhanh như lint, kiểm thử tự động, công cụ chỉ xử lý tệp thay đổi, vì làm việc với agent giống liên tục đón nhân viên mới không có ngữ cảnh. Đưa ngữ cảnh vào repo thay vì để rải rác ở tài liệu, tin nhắn hay trí nhớ của từng người. Tự sở hữu stack thay vì phụ thuộc nặng vào thư viện ngoài, vì agent đã làm giảm chi phí tự xây. Về quản lý, ông tin vai trò này sẽ ngày càng kỹ thuật hơn, vì khi thực thi rẻ đi thì khả năng phán đoán trở thành nút thắt.
Building Software Is Learning
Thorsten Ball công khai một tin nhắn nội bộ gửi đội Amp về việc vì sao xây phần mềm mới luôn là quá trình học. Kịch bản “cần tính năng này”, “làm xong rồi”, “đúng ý tôi” gần như không bao giờ xảy ra khi không có đặc tả; thực tế thường là “không phải ý tôi”, “có ba cách làm, chọn cách nào?” hoặc “dùng thử rồi, không thích”. Lý do là người ta chỉ thực sự hiểu mình đang xây gì trong lúc xây, và muốn tránh hoàn toàn thì phải đặc tả đầy đủ từ đầu, mà đặc tả đầy đủ thì chính là lập trình.
Tin tốt là có thể rút ngắn khoảng thời gian giữa câu “để tôi làm” đầy tự tin và lúc thực tế phản hồi. Thay vì biến mất bốn tuần, hãy làm nguyên mẫu trong một giờ, viết đặc tả trong ba mươi phút, chia nhỏ và phát hành mỗi ngày một phần, cắt phạm vi để tập trung vào điều chưa biết, làm video demo giả, viết trước bài thông báo, hoặc viết ví dụ trong README để thử thiết kế API. Phản hồi có thể đến từ CI, đồng nghiệp, người dùng hay chính bạn khi dùng thử. Tác giả cũng lưu ý: bản thử nghiệm quá nhiều lỗi chỉ đem về báo cáo lỗi chứ không phải phản hồi giá trị, còn giữ nhánh ba tuần rồi gộp 27 commit thì khi CI hỏng sẽ có 27 nguyên nhân tiềm năng. Câu hỏi cần tự đặt liên tục là làm sao nhận phản hồi sớm nhất, thường xuyên nhất để học nhanh hơn.
You Must Fix Your Asserts
Loris Cro phản đối thói quen tắt assert trong môi trường production, lấy Zig làm bối cảnh. Assert là dòng mã khẳng định một sự thật của chương trình, như tiền điều kiện, hậu điều kiện hay bất biến; nếu hệ thống kiểu biểu diễn được ràng buộc thì nên dùng kiểu, còn không thì assert giúp bắt lỗi lập trình, thậm chí hiệu quả hơn nhiều bài kiểm thử đơn vị khi kết hợp fuzzing. Trong Zig, std.debug.assert dựa trên unreachable: ở chế độ có kiểm tra như Debug hay ReleaseSafe, vi phạm assert làm chương trình panic; ở ReleaseFast hay ReleaseSmall, trình biên dịch coi đó là dữ kiện để tối ưu, nhưng nếu assert sai thì chương trình sẽ hoạt động sai khó lường. Khác với C/C++, assert của Zig là hàm thường chứ không phải macro, nên biểu thức truyền vào luôn được tính.
Theo tác giả, có ba lựa chọn: giữ assert như kiểm tra lúc chạy, dùng assert để tối ưu và chấp nhận rủi ro, hoặc tắt hẳn, và lựa chọn thứ ba là tệ nhất. Tắt assert nghĩa là khi điều “không thể xảy ra” xảy ra, chương trình vẫn chạy tiếp trên giả định sai, vẫn lệch khỏi đặc tả, mà không còn tín hiệu nào để phát hiện. Nguy hiểm hơn, người đến sau sẽ viết thêm mã dựa trên assert sai đó, và lỗ hổng có thể lặng lẽ xuất hiện. Tác giả dẫn ví dụ TigerBeetle luôn bật assert, còn Ghostty phát hành bản ReleaseFast. Kết luận: hãy sửa assert sai, dành assert tốn kém cho chế độ debug, ghi log thay vì dừng chương trình khi phù hợp, và chọn chế độ build theo mô hình rủi ro thay vì xóa tín hiệu lỗi.
The Software Engineering Books I keep recommending
Dr Milan Milanović chia sẻ danh sách sách kỹ thuật phần mềm ông vẫn thường giới thiệu, với lý do đọc sách giúp ông có bề rộng và chiều sâu khi thảo luận, ra quyết định, và trong thời AI còn giúp đánh giá, định hướng công cụ AI tốt hơn. Thay vì một danh sách “ai cũng phải đọc”, sách được nhóm theo vấn đề người đọc đang gặp, vì câu hỏi đúng không phải “nên đọc gì” mà là “tôi đang vướng ở đâu”. Phần lớn các cuốn không gắn với công nghệ cụ thể nên kiến thức bền theo thời gian.
Nhóm tư duy viết mã gồm The Pragmatic Programmer, Clean Code (một số quy tắc đã lỗi thời) và A Philosophy of Software Design, cuốn tác giả hay đọc lại nhất. Kiến trúc có Fundamentals of Software Architecture, Software Architecture: The Hard Parts và Domain-Driven Design Quickly; hệ thống và dữ liệu có Designing Data-Intensive Applications bản thứ hai cùng Understanding Distributed Systems dễ tiếp cận hơn. Mảng AI có The Hundred-Page Language Models Book về cách mô hình ngôn ngữ hoạt động và AI Engineering của Chip Huyen về xây ứng dụng AI thực tế. Các nhóm còn lại gồm The Art of Unit Testing cho kiểm thử, Accelerate với bốn chỉ số DORA và Refactoring cho quy trình phát triển, cùng The Software Engineer's Guidebook và Deep Work cho sự nghiệp. Tác giả cũng giới thiệu cuốn sách mới của mình về các định luật trong kỹ thuật phần mềm.
Bài viết đã được viết lại bởi Claude Code với Opus 5.5 vào ngày 27/09/2026.