Newsletter #131

Mời bạn thưởng thức Newsletter #131.

The Codename One JavaScript Port Is Now Free and Open Source

Codename One vừa mở mã nguồn phần chuyển đổi sang JavaScript theo giấy phép GPLv2 kèm ngoại lệ Classpath. Trước đây đây là tính năng chỉ dành cho gói Enterprise, nay mọi gói dịch vụ đều dùng được, và bạn có thể dựng ứng dụng web ngay trên máy mình mà không cần tài khoản Codename One, chỉ bằng một lệnh Maven dùng bộ biên dịch ParparVM đi kèm. ParparVM cũng trở thành trình biên dịch mặc định thay cho TeaVM, kể cả với bản dựng trên đám mây; TeaVM vẫn có thể bật lại bằng tham số dựng để đối chiếu khi cần. Về kiến trúc, mã nguồn Java của ứng dụng chạy trong một Web Worker ở nền, còn phần giao tiếp với trình duyệt nằm trên luồng chính, nhờ vậy mã Java không thể vô tình phụ thuộc vào DOM hay các đối tượng toàn cục của trình duyệt.

Bản phát hành còn có nhiều bổ sung cho framework. API lịch mới truy cập thống nhất lịch trên thiết bị, Google Calendar, Microsoft 365, CalDAV và tệp .ics, hỗ trợ sự kiện lặp, đồng bộ tăng dần, hàng đợi thay đổi khi ngoại tuyến và xử lý xung đột. Bluetooth vào hẳn phần lõi với BLE, GATT, L2CAP, RFCOMM và Web Bluetooth. EditField, RichTextArea và CodeEditor nay tự vẽ và soạn thảo văn bản thay vì đặt ô nhập liệu gốc đè lên, còn RichTextComponent hiển thị được HTML, Markdown, AsciiDoc và RTF mà không cần web view. Lớp proxy hỗ trợ Jakarta Servlet, Node.js, PHP, AWS Lambda, Google Cloud Functions và Cloudflare Workers. ParparVM thêm chuỗi nén, giảm một nửa bộ nhớ cho văn bản ASCII, và Android API 36 sẽ thành mặc định từ tuần sau.

Hacking in the nameOf

Java không có biểu thức nameof như C#, tức cách lấy tên của một định danh dưới dạng chuỗi. Thiếu sót này lộ rõ khi kiểm tra tham số: bạn phải viết cứng tên tham số vào thông điệp ngoại lệ kiểu "name must not be blank", và nếu đổi tên biến mà quên sửa chuỗi thì thông điệp sẽ sai mà trình biên dịch không hề báo. Bài viết thử tự dựng một hàm nameOf cho Java, lấy được tên biến lúc chạy mà không cần công cụ chèn mã hay bộ xử lý annotation.

Cách làm gồm ba bước. Đầu tiên, StackWalker xác định khung gọi đã gọi nameOf, cho biết lớp, tên phương thức, mô tả phương thức và vị trí bytecode. Tiếp theo, tệp .class của lớp đó được đọc vào bộ nhớ và phân tích bằng ClassFile API thành một ClassModel. Cuối cùng, tác giả mô phỏng ngăn xếp toán hạng của JVM: thay vì giá trị thật, mỗi ô chứa một giá trị tượng trưng như tên biến cục bộ hay tên trường, được lan truyền qua từng chỉ thị nạp, lưu, truy cập trường và gọi phương thức, cho tới khi gặp lời gọi nameOf đúng vị trí thì lấy ra tên tượng trưng của đối số. Chính tác giả khuyên đừng dùng cách này: nó dễ vỡ khi xuất hiện opcode mới, phụ thuộc vào thông tin gỡ lỗi tùy chọn như cờ -parameters, có thể hỏng với trình biên dịch khác và gần như chưa được kiểm thử. Đây là một lời giải phức tạp quá mức cho một vấn đề nhỏ, nhưng lại là bài học thú vị về bytecode và các API mới của Java.

Set It and Ship It: How I Let AI Agents Build My Java Services While I Sleep

Sudarshan Shenvi cho rằng agent AI hoàn toàn có thể tự xây dựng dịch vụ Java đạt chất lượng sản phẩm trong những phiên chạy kéo dài nhiều giờ, với điều kiện công việc phải đi qua các cổng kiểm soát nghiêm ngặt; thiếu chúng, bạn chỉ nhận về những thứ nghe rất thuyết phục nhưng vô dụng. Có ba cổng: yêu cầu phải chấm được bằng máy, mỗi mục nêu một hành vi hay giới hạn hiệu năng cụ thể để đánh giá đạt hoặc trượt; một agent phản biện riêng kiểm tra toàn bộ kết quả theo từng mục, vì agent xây dựng không được tự duyệt bài của mình; và một hạ tầng luôn bật, tắt hộp thoại xin phép để phiên chạy năm tiếng không cần người trông, nhưng vẫn giữ quyền truy cập thu hẹp, môi trường cách ly và hạn mức chi tiêu.

Ví dụ minh họa là một kỹ sư nhận phiếu công việc xây dịch vụ đối soát thanh toán. Thay vì viết mã nguồn, anh viết một bản hợp đồng yêu cầu dài mười lăm dòng, nêu rõ các điểm cuối REST, logic đối chiếu, thực thể JPA, độ trễ p95 dưới 500ms, độ phủ kiểm thử 100% và các ràng buộc bảo mật. Anh khởi chạy agent lúc 9 giờ 10 rồi rời đi; vòng phản biện phát hiện lỗi về bằng chứng độ trễ và cấu trúc gói, agent xây dựng tự sửa, và tới 11 giờ 35 anh nhận về bảng kiểm đạt đủ 15/15 mục với độ trễ đo được 410ms. Tác giả cảnh báo lượng token tăng khó lường nên cần giới hạn ngân sách, bắt đầu với nhánh riêng trong môi trường cách ly và luôn giữ bước duyệt của con người.

Software abundance

Nader Dabit, kỹ sư với khoảng mười bốn năm kinh nghiệm và hiện làm việc tại Cognition, nói rằng anh chưa bao giờ hào hứng và lạc quan như lúc này, dù từng lo ngại về tác động của AI lên nghề lập trình. Anh nhận thấy thị trường việc làm đang phân hóa rõ rệt: những người chủ động áp dụng cách làm dựa trên AI và chuyển sang các mảng đang tăng trưởng nhờ AI thì có thu nhập và cơ hội tốt hơn; còn những người bám vào chuyên môn truyền thống như frontend hay backend, hoặc ở lại các công ty lớn chậm thay đổi, thì gặp khó khăn, thậm chí bị cắt giảm.

Luận điểm trung tâm của bài là nghịch lý Jevons: khi năng lực làm ra phần mềm tăng, nhu cầu không giảm mà còn mở rộng. Các đội anh làm việc cùng hiện xuất xưởng số pull request gấp mười tới hai mươi lần trước, và agent chạy trên đám mây giúp cả những người không chuyên cũng triển khai được những việc trước đây cần kỹ sư giàu kinh nghiệm. Tốc độ tăng kéo theo lộ trình sản phẩm dài hơn và tiêu chuẩn chất lượng cao hơn. Theo tác giả, nghề xây dựng phần mềm không biến mất mà đang chuyển mình: AI tự động hóa phần lớn những việc lặp lại mà kỹ sư vốn không thích như sửa lỗi, nâng cấp phụ thuộc hay di trú hệ thống, để con người dồn sức cho óc phán đoán, sự tò mò, hiểu biết ngữ cảnh và gu thẩm mỹ, cũng như có thêm thời gian cho những thử nghiệm sáng tạo từng khiến họ đến với lập trình.

The development pipeline is a production system

Jerry Orr lập luận rằng quy trình phát triển phần mềm chính là một hệ thống sản xuất, nên mỗi khi nó hỏng, đội phát triển cần xử lý với mức khẩn cấp ngang một sự cố ảnh hưởng tới khách hàng. Khi mã nguồn không biên dịch được, lập trình viên không thể làm việc; khi máy chủ QA sập, người kiểm thử không thể xác nhận chất lượng sản phẩm. Với những người đó, đây đúng nghĩa là sự cố sản xuất, và bất kỳ thành phần nào cản trở chặng đường từ phát triển tới triển khai, từ hệ thống quản lý công việc, công cụ CI/CD đến bộ kiểm thử bị lỗi, đều là một sự cố nghiêm trọng.

Tác giả so sánh với ngành chế tạo, nơi các nhà máy từ lâu đã có quy trình bài bản để giảm thời gian dây chuyền lắp ráp phải ngừng, trong khi các đội phần mềm lại thường xem nhẹ hạ tầng nội bộ của chính mình. Lời khuyên là xác định và ưu tiên mọi thành phần thiết yếu của quy trình phát triển: nền tảng quản lý công việc và thay đổi, công cụ cho lập trình viên như IDE và hệ thống dựng, kho gói và hạ tầng cục bộ, công cụ CI/CD, môi trường QA và bộ kiểm thử. Đội ngũ nên áp dụng cách quản lý sự cố có hệ thống vốn quen thuộc trong quản trị dịch vụ CNTT và sản xuất cho chính hạ tầng phát triển, vì một quy trình phát triển hỏng trực tiếp ngăn phần mềm tới tay khách hàng.

How LLMs Figure Out What You Mean - No Math Degree Required

Bài viết giải thích cách một mô hình ngôn ngữ lớn hiểu ý nghĩa của câu mà không cần kiến thức toán, dùng câu hỏi “Do lions roar?” làm ví dụ xuyên suốt. Bước đầu tiên là biến chữ thành số: văn bản được cắt thành các token, mỗi token có một mã số. Nhưng một con số đơn lẻ không mang ý nghĩa, vì số 2 chẳng có lý do gì để “giống” số 3, nên mỗi token được chuyển thành một vector, tức một dãy số mà các vị trí cùng nhau nắm bắt ý nghĩa của từ, và các từ gần nghĩa sẽ có vector gần giống nhau. Có thể hình dung vector như mũi tên: từ gần nghĩa chỉ cùng hướng, từ trái nghĩa chỉ ngược hướng, từ không liên quan tạo góc vuông.

Để đo độ giống nhau, mô hình dùng tích vô hướng, được minh họa bằng hình ảnh hai người cùng đẩy một chiếc xe đẩy hàng: đẩy cùng hướng thì lực cộng dồn và điểm cao, đẩy chéo góc thì hiệu quả giảm, còn đẩy ngược chiều thì triệt tiêu và cho điểm âm. Điểm thô chỉ có nghĩa khi so với các điểm khác, nên mô hình dùng hàm softmax để đổi chúng thành tỉ lệ phần trăm; tác giả ví mỗi từ có đúng một đô la sự chú ý để chia thành các lát bánh pizza cho những từ khác tùy mức độ gần gũi. Nhờ vậy, trong câu ví dụ, “roar” dồn phần lớn sự chú ý cho “lions” chứ không phải “Do”. Các vector ban đầu hoàn toàn ngẫu nhiên và chỉ dần được chỉnh đúng qua quá trình huấn luyện bằng lan truyền ngược trên khối lượng văn bản khổng lồ.

Meet Alice. Alice is impatient.

Marc Brooker mở đầu bằng hai tình huống quen thuộc với người vận hành dịch vụ. Bạn đo được độ trễ trung bình 100ms, nhưng khách hàng Alice nói cô phải chờ trung bình một giây. Bạn báo cáo thời gian khôi phục trung bình dưới một phút, nhưng khách hàng Alex lại thấy mỗi lần sự cố kéo dài khoảng một tiếng. Theo tác giả, cả hai bên đều đúng: người vận hành đếm theo sự kiện, còn khách hàng trải nghiệm theo thời gian. Đây là nghịch lý kiểm tra: sự cố hay phản hồi chậm càng kéo dài thì càng bị nhiều khách hàng, hoặc nhiều lần thử lại, bắt gặp, nên thứ khách hàng cảm nhận là phiên bản phân phối đã được đánh trọng số theo độ dài.

Về mặt toán học, nếu người vận hành đo được giá trị trung bình E[X], thì khách hàng trải nghiệm E[X²] / (2E[X]), có thể viết lại thành một nửa của E[X] + Var(X)/E[X]. Như vậy khoảng cách giữa hai góc nhìn tỉ lệ với phương sai chia cho trung bình, và phương sai lớn sẽ đẩy thời gian chờ cảm nhận lên rất cao. Tác giả kèm công cụ và đoạn mã Python mô phỏng: với trung vị 30 phút và p99 600 phút, khách hàng cảm nhận thời gian khôi phục khoảng 6 tiếng, trong khi MTTR đo được chỉ một tiếng. Hệ thống thực thường có phân phối đuôi nặng, nên phép đo cắt bớt ngoại lai sẽ che mất phần đuôi vốn quyết định trải nghiệm. Sự cố ngắn đã có cách xử lý tốt, còn sự cố kéo dài thì cả ngành chưa giải được, và đó mới là chỗ nên đầu tư.

Harness Engineering Deep Dive: Where The Term Came From, And How To Actually Build One

Bài viết của Lukas Niessen bắt đầu từ công thức đã thành chuẩn: agent bằng mô hình cộng harness. Harness là mọi thứ còn lại: vòng lặp, công cụ, môi trường cách ly, lắp ghép ngữ cảnh, phân quyền, kiểm thử, trạng thái bền vững, vết chạy, hạn mức chi phí và cổng phê duyệt. Mô hình là thứ bạn đi thuê, còn harness là thứ bạn hoàn toàn sở hữu, nên khi agent thất bại, thay vì chờ mô hình tốt hơn, bạn có thể sửa môi trường để lỗi đó không thể xảy ra về mặt cấu trúc. Thuật ngữ được định hình đầu năm 2026 qua các bài của Mitchell Hashimoto, Birgitta Böckeler và một báo cáo của OpenAI, như bước tiếp theo sau prompt engineering và context engineering. Ranh giới giữa wrapper và harness nằm ở vòng lặp khép kín: khi đầu ra của mô hình trở thành quyết định được hệ thống thực thi, rồi mô hình quan sát kết quả và quyết định tiếp, mọi mối lo của harness bắt đầu có hiệu lực.

Phần kỹ thuật đi qua bảy vấn đề của vòng lặp ngây thơ: cảm biến phải chắt lọc tín hiệu thay vì đổ nguyên đầu ra vào ngữ cảnh; lịch sử hội thoại chỉ là bộ nhớ đệm, còn sổ cái mới là sự thật; agent không được tự quyết định là đã xong; thu hẹp không gian hành động bằng lược đồ công cụ thay vì viết lời nhắc dài hơn; hạn mức kèm phát hiện đình trệ; cổng công cụ phân tầng theo rủi ro; và coi vết chạy là đơn vị gỡ lỗi. Theo tác giả, chất lượng harness có thể làm lượng token tiêu thụ chênh nhau 10–50 lần.

How big is a Git commit?

Một commit của Git thực sự chiếm bao nhiêu dung lượng? Để trả lời, Dave Gauer đo kích thước thư mục .git bằng du -sb qua nhiều kịch bản, chỉ dùng đối tượng rời và không dùng packfile để thấy rõ cách lưu trữ cốt lõi. Một kho mới khởi tạo tốn khoảng 64 KB; commit 250 tệp nhỏ trong 5 thư mục tốn thêm chừng 47 KB; một thay đổi nhỏ trong một tệp tốn 17 KB, trong khi ở kho chỉ có một tệp thì cùng loại thay đổi ba byte chỉ cần khoảng 8,7 KB.

Nguyên nhân nằm ở cấu trúc đối tượng: khi commit thay đổi của một tệp, Git tạo bốn đối tượng gồm chính commit, một cây cho thư mục chứa tệp liệt kê mọi tệp trong đó, nội dung tệp và cây thư mục gốc, tất cả được nén bằng zlib. Vì vậy dù thay đổi rất nhỏ, phần phụ trội vẫn tính bằng kilobyte và tăng theo độ phức tạp của cây thư mục. Với tệp lớn, khả năng nén rất ấn tượng: tệp nhị phân khoảng 580 KB được lưu thành 298 KB, còn tệp mã nguồn 16,4 MB chỉ còn 1,6 MB, tức khoảng 10% kích thước gốc. Nhìn chung, với commit lớn gồm ít tệp, dữ liệu trong .git chỉ bằng 50% tới 10% kích thước tệp chưa nén, tùy khả năng nén của dữ liệu. Tác giả kết luận Git lưu trữ rất hiệu quả, nhất là với văn bản, và packfile còn tiết kiệm hơn nữa nhờ loại bỏ phần trùng lặp giữa các đối tượng.

How I Find Problems to Solve as a Staff Engineer

Lalit Maganti chia sẻ cách anh tìm ra những bài toán đáng giải khi làm kỹ sư staff. Thay vì chặn lịch để “suy nghĩ chiến lược”, anh hành xử như một miếng bọt biển: lắng nghe dòng tiếng ồn hằng ngày trong các cuộc họp, kênh chat và email, thấm dần những vấn đề mọi người đang gặp. Khi điều gì đó chạm tới mảng của mình, anh đào sâu tìm vấn đề thực sự thay vì chấp nhận yêu cầu bề mặt, có khi ngồi cùng các đội xem họ làm việc hoặc tự tay thử gỡ lỗi để phân biệt nhu cầu thật với giải pháp người ta đề xuất. Anh cũng cố ý không lao vào xây dựng ngay sau yêu cầu đầu tiên, vì đội hăng hái nhất chưa chắc đại diện cho ưu tiên của tổ chức; khi cùng một vấn đề xuất hiện độc lập ở nhiều đội, đó mới là tín hiệu mạnh.

Những buổi đi bộ giúp anh nhận ra mối liên hệ giữa các vấn đề tích lũy được. Với Perfetto, trong hai năm các đội xin nhiều tính năng giao diện khác nhau như ghim track, mức phóng tùy chỉnh hay tổng hợp theo ý riêng, và cuối cùng anh nhận ra nhu cầu thật là khả năng mở rộng giao diện. Trước khi cam kết nguồn lực, anh kiểm chứng ý tưởng bằng bản mẫu dùng xong bỏ và xin ý kiến quản lý cùng các đội liên quan. Mỗi lần giải đúng vấn đề, anh được mời vào thảo luận sớm hơn và có ảnh hưởng tới lộ trình mà không cần tự làm mọi thứ. Anh lưu ý cách này hợp nhất với môi trường tự chủ từ dưới lên.

Golang Maps: How Swiss Tables Replaced the Old Bucket Design

Map nằm trên đường đi nóng của gần như mọi chương trình Go, và Go 1.24 đã thay thiết kế bên trong của nó bằng Swiss Table. Thiết kế cũ dùng một mảng bucket, mỗi bucket chứa tối đa 8 cặp khóa–giá trị kèm byte siêu dữ liệu; khi bucket đầy, bộ chạy cấp phát một bucket tràn và nối chuỗi vào. Cách này ổn định, nhưng chuỗi tràn buộc chương trình đuổi theo con trỏ qua các vùng bộ nhớ không liền nhau, gây trượt bộ nhớ đệm, và hệ số tải chỉ đạt khoảng 81% trước khi chi phí va chạm trở nên đáng kể.

Swiss Table tách giá trị băm làm hai: một phần quyết định vị trí, phần còn lại là vân tay ngắn lưu trong siêu dữ liệu. Dữ liệu được xếp thành các nhóm 8 ô gồm byte điều khiển, ô khóa và ô giá trị; khi tra cứu, chương trình quét byte điều khiển trước, chỉ so sánh khóa đầy đủ ở những ô có vân tay khớp, và dừng ngay khi gặp ô trống. Phần lớn ô bị loại ở bước siêu dữ liệu rẻ này, đường dò phẳng giúp tận dụng bộ nhớ đệm tốt hơn, và hệ số tải tăng lên khoảng 87,5%. Thay vì một bảng lớn phải băm lại toàn bộ khi mở rộng, Go dùng nhiều bảng nhỏ với một lớp thư mục định tuyến để việc mở rộng diễn ra cục bộ và tăng dần, vẫn giữ ngữ nghĩa của range. Thời gian chạy tổng thể giảm khoảng 2–3%, còn thao tác trên map lớn nhanh hơn 30–35%. Đổi lại, mẫu xóa nhiều để lại bia mộ làm đường dò dài ra cho tới khi được dọn.

The new rules of context engineering for Claude 5 models

Khi bạn gửi tin nhắn cho Claude, lời nhắc chỉ là một phần nhỏ trong ngữ cảnh mà nó nhận được; phần lớn được lắp ghép từ system prompt, skill, tệp CLAUDE.md, bộ nhớ và nhiều nguồn khác, và việc thiết kế phần đó gọi là context engineering. Trong bài viết này, một kỹ sư Anthropic cho biết họ đã xóa hơn 80% system prompt của Claude Code cho các mô hình như Claude Opus 5 và Claude Fable 5 mà không làm giảm điểm trên các bài đánh giá lập trình. Lý do là trước đây họ ràng buộc Claude quá mức: trong cùng một yêu cầu, system prompt, skill và lời nhắc của người dùng có thể mâu thuẫn, chẳng hạn “viết tài liệu khi phù hợp” bên cạnh “KHÔNG thêm chú thích”, khiến mô hình phải cân nhắc thêm, trong khi mô hình mới đủ khả năng tự phán đoán.

Bài viết đối chiếu các thói quen cũ với cách làm mới. Thay vì đặt luật cứng, hãy để Claude tự phán đoán; chỉ dẫn mới chỉ nói “viết mã nguồn giống mã nguồn xung quanh”. Thay vì đưa ví dụ dùng công cụ, vốn bó hẹp không gian khám phá, hãy thiết kế tham số công cụ biểu đạt hơn. Thay vì dồn mọi thứ lên đầu, hãy tiết lộ dần: phần kiểm chứng và rà soát mã nguồn được tách thành skill riêng, một số công cụ chỉ được nạp khi cần. Bộ nhớ nay được lưu tự động, và tài liệu tham chiếu có thể là hiện vật HTML hay bộ kiểm thử thay vì tệp markdown đơn giản. Hãy giữ CLAUDE.md gọn, dành phần lớn cho những điểm dễ vấp trong kho mã nguồn thay vì những gì Claude tự nhìn ra được.

The git history command deserves more attention

Làm nhiều thay đổi song song trong Git thường phải nhảy qua lại giữa các nhánh và rebase đầy rủi ro, nên jj (Jujutsu) được nhắc tới nhiều như một lựa chọn thay thế. Lalit Maganti cho rằng lệnh thử nghiệm git history, ra mắt qua Git 2.54 (tháng 4/2026, với reword và split) và Git 2.55 (tháng 6/2026, với fixup), mang lại nhiều lợi ích tương tự mà không cần đổi cả quy trình, nhưng lại ít được bàn tới. Với fixup, bạn đưa thay đổi vào vùng chờ bằng git add rồi chạy git history fixup <commit> để gộp chúng vào một commit cũ; mọi nhánh cục bộ nằm dưới commit đó đều được rebase theo, vượt xa git rebase --update-refs. Lệnh reword mở trình soạn thảo để sửa thông điệp của một commit cũ rồi dựng lại cả chồng commit phía trên cùng các nhánh, còn split chia một commit thành hai bằng cách chọn từng khối thay đổi theo kiểu tương tác.

Điểm chung của cả ba lệnh là tính nguyên tử: chúng không bao giờ để kho ở trạng thái hỏng dở dang, vì sẽ từ chối mọi thao tác có thể sinh xung đột. Đó cũng là giới hạn lớn nhất so với jj, vốn coi xung đột là đối tượng hạng nhất và cho phép giải quyết sau; ngoài ra fixup không làm việc được khi có commit gộp, và Git chưa có nhật ký thao tác để hoàn tác dễ dàng. Tài liệu của Git cho biết giới hạn về xung đột có thể được gỡ bỏ trong tương lai. Dù vậy, đây vẫn là bước tiến đáng kể vì nó đưa những lợi ích khiến người ta tìm đến công cụ khác vào thẳng bản phân phối chuẩn của Git.


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.

Made by miti99 with ❤️
Built with Hugo
Theme Stack thiết kế bởi Jimmy