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 và đưa nó vào mọi gói dịch vụ, kể cả gói miễn phí. Bản dựng trên ParparVM trở thành lựa chọn mặc định, còn TeaVM vẫn được giữ lại như phương án dự phòng. Thay đổi đáng chú ý nhất với người dùng là bạn có thể dựng bản JavaScript ngay trên máy mình mà không cần tài khoản, chỉ với một lệnh Maven đặt codename1.buildTarget=local-javascript. Về kiến trúc, mã nguồn Java của ứng dụng chạy trong một Web Worker ở nền và trao đổi với luồng chính của trình duyệt qua một cầu nối thông điệp nhẹ, nhờ vậy các phụ thuộc Java được tách khỏi DOM và các đối tượng toàn cục của trình duyệt. Trình dựng mới còn sinh sẵn lớp bọc proxy cho nhiều môi trường triển khai: Jakarta Servlet, Node, PHP, AWS Lambda, Google Cloud Functions và Cloudflare Workers.
Bản phát hành đi kèm khá nhiều bổ sung cho framework. Gói com.codename1.calendar cung cấp một API thống nhất để truy cập lịch trên thiết bị, Google Calendar, lịch Microsoft, máy chủ CalDAV và tệp .ics, có hỗ trợ lặp lịch, đồng bộ tăng dần, hàng đợi thay đổi khi ngoại tuyến và xử lý xung đột. Hỗ trợ Bluetooth mở rộng sang BLE ở cả vai trò trung tâm lẫn ngoại vi, GATT, luồng L2CAP, RFCOMM cổ điển và Web Bluetooth. EditField, RichTextArea và CodeEditor nay vẽ và soạn thảo văn bản hoàn toàn bên trong thành phần của Codename One thay vì đặt một ô nhập liệu gốc đè lên, giúp ứng dụng tự kiểm soát việc hiển thị như tô màu cú pháp. Ngoài ra ParparVM đã hỗ trợ chuỗi nén, lưu ký tự trong mảng byte khi mọi ký tự vừa một byte, giảm một nửa bộ nhớ cho các chuỗi thông thường. Android API 36 sẽ thành mặc định trong tuần tới.
Hacking in the nameOf
C# có biểu thức nameof cho phép lấy tên của một định danh dưới dạng chuỗi, còn Java thì không. Thiếu sót này gây phiền phức trong một tình huống rất quen thuộc: khi kiểm tra tham số đầu vào, tên tham số thường bị viết cứng vào thông điệp ngoại lệ, nên mỗi lần đổi tên biến là thông điệp lại sai mà trình biên dịch không hề cảnh báo. Bài viết này thử tự dựng một hàm nameOf cho Java để viết được throw new IllegalArgumentException(nameOf(name) + " must not be blank") mà không phải nhắc lại tên biến bằng chuỗi.
Cách làm dựa trên việc soi mã bytecode lúc chạy, kết hợp hai API của Java: StackWalker để xác định khung gọi hiện tại, và ClassFile API để nạp tệp .class tương ứng rồi phân tích thành một ClassModel. Từ đó tác giả mô phỏng lại máy ngăn xếp của JVM, duyệt qua từng chỉ thị bytecode và lan truyền các giá trị tượng trưng trên một ngăn xếp toán hạng giả lập, cho tới khi gặp lời gọi nameOf thì lấy ra tên tượng trưng của đối số. Tác giả cũng rất thẳng thắn về giới hạn: đoạn mã gần như chưa được kiểm thử, dễ vỡ khi gặp opcode mới và không chạy ổn định trên mọi cấu hình JVM, nên tuyệt đối không nên dùng cho sản phẩm thật. Giá trị của bài nằm ở chỗ nó là một chuyến tham quan thú vị vào bytecode và các API ít người dùng của Java hiện đại.
Set It and Ship It: How I Let AI Agents Build My Java Services While I Sleep
Tác giả chia sẻ cách để các agent AI tự viết dịch vụ Java suốt nhiều giờ mà vẫn cho ra kết quả dùng được. Theo anh, các agent hiện nay hoàn toàn có thể chạy liên tục năm tiếng và xây gần như mọi thứ bạn yêu cầu, nhưng chỉ khi bạn viết yêu cầu thật cẩn thận và ép công việc đi qua những cổng kiểm tra nghiêm ngặt trước khi nhận lại. Bỏ qua phần đó thì bạn chỉ nhận về năm tiếng sản phẩm nghe rất thuyết phục nhưng vô dụng. Ba cổng kiểm tra bổ trợ nhau: yêu cầu phải kiểm chứng được bằng máy, một agent phản biện riêng biệt chỉ làm nhiệm vụ đối chiếu kết quả với danh sách yêu cầu, và một môi trường luôn bật để phiên làm việc chạy không gián đoạn.
Điểm mấu chốt là chất lượng của danh sách yêu cầu, vì nó chính là bản hợp đồng. Thay vì viết “API phải nhanh và an toàn”, hãy viết “mọi điểm cuối phản hồi dưới 200ms ở p95 với 100 yêu cầu đồng thời, mọi đầu vào được kiểm tra ở phía máy chủ, không có bí mật nào lọt vào nhật ký, mọi tuyến đều yêu cầu xác thực trừ /health”. Agent phản biện phải là một agent khác — agent xây dựng không được tự chấm bài của mình — và phải duyệt từng mục, đạt hay trượt rõ ràng, chỉ trả kết quả về khi 100% mục đạt. Tác giả cũng khuyên tắt các hộp thoại xin phép để phiên chạy không cần người trông, nhưng vẫn giữ nguyên các rào an toàn: quyền hạn thu hẹp, môi trường cách ly, nhánh riêng và giới hạn chi phí. Anh nhấn mạnh cần theo dõi sát lượng token, vì mỗi yêu cầu bị trượt rồi làm lại là thêm một vòng xử lý và chi phí tăng rất nhanh.
Software abundance
Sau khoảng tám tháng dao động giữa lạc quan và bi quan, tác giả — một kỹ sư với mười bốn năm kinh nghiệm, hiện làm ở Cognition — kết luận rằng mình đang ở phía lạc quan nhất từ trước tới nay. Về thị trường việc làm, anh quan sát thấy sự phân hóa rất rõ: những người chủ động đưa AI vào cách làm việc, chuyển sang các mảng và sản phẩm mới đang tăng trưởng, thì thu nhập và cơ hội nhiều hơn bao giờ hết; ngược lại những người bám chặt vào một danh xưng hẹp kiểu “frontend” hay “backend”, hoặc ở lại các tổ chức lớn chậm thay đổi, thì gặp khó khăn hoặc bị cắt giảm. Cùng một ngành nghề nhưng hai nhóm này kể hai câu chuyện hoàn toàn trái ngược.
Nhận định thú vị nhất của bài là về nghịch lý Jevons: khi viết mã nguồn rẻ và nhanh hơn, lượng phần mềm được tạo ra không giảm mà bùng nổ. Số công ty phần mềm tăng vọt, các đội nhóm mà anh làm việc cùng thường tăng số pull request đã xuất xưởng lên mười tới hai mươi lần, và số người bắt đầu viết phần mềm cũng tăng mạnh. Nhưng mã nguồn dễ viết không có nghĩa là phần mềm tốt dễ làm: đối thủ của bạn cũng xuất xưởng nhiều hơn hai mươi lần, nên lộ trình sản phẩm dài ra và tiêu chuẩn chất lượng bị đẩy lên cao hơn. Tác giả từng lo rằng nghề thủ công xây dựng phần mềm sẽ biến mất, nhưng anh nhận ra điều ngược lại: phần bị tự động hóa chủ yếu là những việc kỹ sư vốn không thích — sửa lỗi, di trú hệ thống, nâng cấp phụ thuộc, các tác vụ lặp đi lặp lại. Thứ agent vẫn thiếu là óc phán đoán, sự tò mò, ngữ cảnh và gu thẩm mỹ của một con người gắn với thế giới bên ngoài.
The development pipeline is a production system
Công việc cốt lõi của một lập trình viên là tạo ra giá trị qua việc phát triển tính năng và sửa lỗi, nhưng điều đó không thể diễn ra khi quy trình phát triển bị hỏng — nghĩa là các công cụ, hệ thống dựng và môi trường kiểm thử phục vụ việc tạo ra phần mềm. Tác giả lập luận rằng nếu một sự cố trên môi trường thật luôn được xử lý ngay lập tức, thì hạ tầng phục vụ chính đội phát triển cũng xứng đáng nhận mức độ khẩn cấp tương tự. Khi mã nguồn không biên dịch được hoặc máy chủ kiểm thử sập, cả lập trình viên lẫn người kiểm thử đều không làm việc được — với họ, đó đúng nghĩa là một sự cố sản xuất.
Bài viết mượn hình ảnh từ ngành chế tạo: các nhà máy có cả bộ quy trình dày dặn để giảm thời gian dây chuyền lắp ráp ngừng chạy, trong khi ngành phần mềm lại thường bỏ qua những quy trình tương đương cho hạ tầng nội bộ của mình. Đề xuất của tác giả là bảo vệ mọi thành phần trong quy trình — hệ thống quản lý công việc, IDE, công cụ CI/CD, bộ kiểm thử và môi trường QA — ở mức ưu tiên như môi trường thật. Khi nhìn nhận mỗi bước từ lúc khách hàng nêu yêu cầu đến khi triển khai đều là hạ tầng thiết yếu, tổ chức sẽ phân bổ nguồn lực và quy trình ứng phó hợp lý hơn để giữ được tốc độ giao phần mềm.
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 được câu bạn viết, bằng ngôn ngữ đời thường thay vì công thức toán. Đầu tiên, từ ngữ được cắt thành token và mỗi token nhận một mã số, sau đó mã số này được biến thành một vector — một dãy số mà mỗi vị trí mang một phần ý nghĩa. Nhờ vậy mô hình có thể so sánh các từ với nhau: từ có nghĩa gần nhau thì vector cũng gần nhau. Tác giả dùng hình ảnh mũi tên để hình dung: những từ mang nghĩa tương tự có mũi tên chỉ về cùng hướng, còn những từ không liên quan thì chỉ theo hướng khác, thậm chí vuông góc với nhau.
Để chấm điểm mức độ giống nhau giữa hai mũi tên, mô hình dùng tích vô hướng: nếu hai mũi tên đẩy về cùng một hướng thì lực cộng lại thành một cú đẩy mạnh, tức điểm số cao. Các điểm thô này sau đó được chuyển thành trọng số tương đối qua hàm softmax, và đây chính là cơ chế attention: mỗi từ có một đồng bạc để chia, nó dành phần lớn cho những từ gần nghĩa và phần nhỏ cho những từ xa. Chẳng hạn trong một câu, từ “roar” sẽ dồn phần lớn sự chú ý cho “lions” chứ không phải cho “Do”. Điều thú vị là các vector ban đầu hoàn toàn ngẫu nhiên; chúng chỉ dần dần được xếp đúng chỗ nhờ quá trình huấn luyện trên khối lượng văn bản khổng lồ thông qua lan truyền ngược.
Meet Alice. Alice is impatient.
Marc Brooker mở đầu bằng một nghịch lý quen thuộc với ai từng vận hành dịch vụ: bạn đo được thời gian phản hồi trung bình 100ms, nhưng khách hàng Alice lại nói cô ấy phải chờ trung bình một giây. Tương tự, bạn công bố thời gian khắc phục sự cố trung bình dưới một phút, còn khách hàng Alex thì kể rằng mỗi lần sự cố kéo dài cả tiếng. Câu trả lời của tác giả là cả hai đều đúng — chỉ đơn giản là hai bên đang đo hai thứ khác nhau.
Nguyên nhân nằm ở việc trải nghiệm của khách hàng bị đánh trọng số theo thời gian. Khi dịch vụ chậm hoặc sập trong một khoảng dài, khách hàng sẽ chạm vào khoảng đó nhiều lần, và số lần chạm tỉ lệ thuận với độ dài của nó — các khoảng ngắn thì hiếm khi rơi trúng. Nói cách khác, Alice và Alex không trải nghiệm phân phối độ trễ f(t) của bạn, mà trải nghiệm phiên bản đã nhân trọng số theo t của phân phối đó, với kỳ vọng bằng E[X²] / (2E[X]). Đây chính là nghịch lý lấy mẫu theo độ dài. Tác giả kèm theo một đoạn mô phỏng Python cho một máy chủ sập theo chu kỳ với khách hàng đến theo tiến trình Poisson, cho thấy rõ mỗi lần sự cố bị lấy mẫu nhiều lần và mỗi mẫu lại được đánh trọng số theo phần thời gian còn lại của sự cố. Hệ quả thực tế là độ phân tán, đặc biệt là độ nặng của đuôi phân phối, ảnh hưởng tới cảm nhận của khách hàng nhiều hơn hẳn giá trị trung bình.
Harness Engineering Deep Dive: Where The Term Came From, And How To Actually Build One
Bài viết dài này mổ xẻ khái niệm harness engineering, đi từ công thức đã thành chuẩn: agent bằng mô hình cộng harness. Mô hình là phần bạn đi thuê, còn harness — vòng lặp, công cụ, môi trường cách ly, cách lắp ghép ngữ cảnh, phân quyền, các bài kiểm thử phải vượt qua, trạng thái sống sót sau sự cố, nhật ký vết chạy, hạn mức chi phí và các cổng phê duyệt — là phần bạn sở hữu hoàn toàn. Từ đó, mỗi khi agent hỏng việc, bạn có hai lựa chọn: ngồi chờ mô hình tốt hơn, hoặc sửa môi trường để lỗi đó không còn xảy ra được về mặt cấu trúc. Tác giả cũng vạch ranh giới rõ giữa “wrapper” và “harness”: chừng nào đầu ra của mô hình chỉ là sản phẩm cuối thì đó là wrapper; khi đầu ra trở thành một quyết định được hệ thống thi hành rồi mô hình quan sát kết quả và quyết định tiếp, vòng lặp đã khép lại và mọi mối bận tâm về harness bắt đầu có hiệu lực.
Phần kỹ thuật đi qua bảy vấn đề, xuất phát từ vòng lặp ngây thơ mà ai cũng viết lần đầu. Cảm biến phải là bộ biến đổi chứ không phải cái ống: đừng đổ nguyên đầu ra của pytest vào ngữ cảnh mà hãy trả về tín hiệu đã chắt lọc kèm hành động tiếp theo. Lịch sử hội thoại chỉ là bộ nhớ đệm, còn sổ cái mới là sự thật — nó được tạo lại mỗi lượt chứ không nối thêm, và phải sống sót qua nén ngữ cảnh lẫn sự cố tiến trình. Agent không được tự tuyên bố đã xong; điều kiện kết thúc phải do máy kiểm chứng. Thay vì viết lời nhắc dài hơn, hãy thu hẹp không gian hành động bằng lược đồ công cụ chặt chẽ. Cuối cùng là hạn mức kèm phát hiện đình trệ, cổng công cụ phân tầng theo mức rủi ro, và lưu vết chạy như một hiện vật hạng nhất để gỡ lỗi.
How big is a Git commit?
Một commit của Git thực sự chiếm bao nhiêu dung lượng? Câu hỏi nghe đơn giản nhưng không có con số cố định, vì Git lưu đối tượng dưới dạng dữ liệu đã nén bằng zlib, nên kích thước phụ thuộc vào mức độ nén được và độ trùng lặp của tệp. Tác giả đo đạc bằng cách dùng đối tượng rời, tức tắt hẳn packfile, để nhìn rõ phần chi phí phụ trội. Kết quả: khởi tạo một kho Git tốn khoảng 64 KB; commit 250 tệp rất nhỏ tốn thêm chừng 47 KB; sửa đúng ba byte trong một tệp giữa thư mục 50 tệp lại tốn 17 KB, trong khi cùng thay đổi đó ở kho chỉ có một tệp chỉ tốn 8,7 KB.
Lý do nằm ở cấu trúc đối tượng: mỗi commit sinh ra một đối tượng commit, các đối tượng cây liệt kê nội dung thư mục, và các đối tượng blob chứa dữ liệu tệp — nên cây thư mục càng phức tạp thì phần phụ trội càng lớn. Với tệp lớn thì khả năng nén lại rất ấn tượng: một tệp nhị phân 584 KB cho ra commit 298 KB, còn một tệp mã nguồn 16,4 MB chỉ còn khoảng 1,6 MB sau khi commit. Nhìn tổng thể, dữ liệu Git lưu thường chỉ bằng 50% đến 10% kích thước gốc của các tệp đã commit. Kết luận thực dụng của bài: phần phụ trội tính bằng kilobyte chỉ đáng bận tâm với những commit cực nhỏ, và con số cụ thể phụ thuộc vào độ phức tạp của cây thư mục bị ảnh hưởng.
How I Find Problems to Solve as a Staff Engineer
Một kỹ sư staff hỏi Lalit Maganti rằng làm sao để tìm ra những bài toán đáng giải, ngoài những việc đã được giao sẵn. Câu trả lời của anh không phải là chặn lịch để “suy nghĩ chiến lược”, mà là cách tiếp cận anh gọi vui là làm miếng bọt biển: thấm dần mọi tiếng ồn trong tổ chức cho tới khi các mẫu hình tự lộ ra. Anh lắng nghe kỹ trong các cuộc họp, kênh chat và buổi trình bày, vì người ta rất thích nói về vấn đề họ đang gặp — nhưng thường họ chỉ nêu giải pháp mong muốn chứ không nói nguyên nhân gốc. Vì vậy anh đào sâu thêm, đôi khi ngồi cùng các đội để xem họ làm việc thực tế, đồng thời giữ quan hệ với những người ở nhiều bộ phận khác nhau để nhìn được bức tranh rộng.
Điểm quan trọng thứ hai là để các vấn đề tích tụ lại thay vì lao vào ngay yêu cầu đầu tiên — anh từng trả giá vì vội vàng. Anh giữ một danh sách vấn đề trong đầu và chờ xem điều gì lặp lại ở nhiều đội khác nhau; những vấn đề thực sự đáng giải sẽ tự quay lại, còn yêu cầu nhất thời thì lặng đi. Khi nhiều yêu cầu khác nhau chia sẻ cùng một hình dạng bên dưới, giải pháp gọn gàng sẽ xuất hiện — như phần mở rộng giao diện của Perfetto, nơi nhiều đội xin những tính năng khác nhau nhưng thực chất đều muốn khả năng tùy biến riêng. Trước khi cam kết làm lớn, anh thử nghiệm niềm tin của mình bằng bản mẫu hoặc trao đổi không chính thức với quản lý, đồng nghiệp và các đội liên quan, và sẵn sàng bỏ ý tưởng nếu nhu cầu không thật hoặc không khả thi. Cách làm này tạo ra vòng lặp tích cực: giải đúng vấn đề thật giúp xây niềm tin, mở rộng tầm nhìn, và dần dần cho anh tiếng nói trong lộ trình sản phẩm mà không cần tự tay làm mọi thứ.
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, nên Go 1.24 mang đến một trong những thay đổi lớn nhất nhiều năm qua ở phần lõi: thay thiết kế bucket kèm chuỗi tràn cũ bằng một thiết kế lấy cảm hứng từ Swiss Table. Giao diện bên ngoài giữ nguyên — bạn vẫn viết map[K]V, vẫn dùng make, truy cập, delete và range như cũ — nhưng đường tra cứu và chèn được sắp xếp lại quanh siêu dữ liệu gọn hơn và bố cục thân thiện với bộ nhớ đệm. Thiết kế cũ chia map thành các bucket 8 ô, khi đầy thì cấp phát bucket tràn và nối chuỗi; cách này ổn định và cho phép mở rộng tăng dần, nhưng chuỗi tràn kéo theo việc đuổi theo con trỏ và trượt bộ nhớ đệm, đồng thời hệ số tải hữu dụng bị chặn quanh mức 81%.
Swiss Table tổ chức các ô thành nhóm cố định 8 ô, mỗi nhóm có một từ điều khiển gồm tám byte liền nhau mô tả trạng thái ô và lưu vân tay h2 lấy từ hàm băm. Khi tra cứu, chương trình đọc các byte điều khiển của cả nhóm, chỉ so sánh khóa thật ở những vị trí có vân tay trùng, và dừng ngay khi gặp ô trống. Nhờ vậy phần lớn ô bị loại từ giai đoạn siêu dữ liệu rất rẻ, số lần nạp khóa đầy đủ giảm hẳn, vòng lặp dò tìm đơn giản và dễ dự đoán nhánh, còn hệ số tải hữu dụng nâng lên khoảng 87,5%. Go cũng có những điều chỉnh riêng: thay vì một bảng khổng lồ phải băm lại toàn bộ khi mở rộng, bộ chạy chia thành nhiều bảng nhỏ với một lớp thư mục định tuyến khóa, nên chỉ phân đoạn đang chịu áp lực mới phải tách — giữ được độ trễ ổn định và ngữ nghĩa của range. Đổi lại, mẫu xóa nhiều tạo ra nhiều bia mộ vẫn có thể kéo dài đường dò cho tới khi bảng được dọn.
The new rules of context engineering for Claude 5 models
Khi bạn gửi một tin nhắn cho Claude, lời nhắc của bạn 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, các skill, tệp CLAUDE.md, bộ nhớ và nhiều nguồn khác. Bài viết này của một kỹ sư Anthropic chia sẻ điều họ rút ra khi làm việc với thế hệ mô hình Claude 5, và con số gây chú ý nhất là họ đã xóa hơn 80% system prompt của Claude Code mà các bài đánh giá về lập trình không hề giảm điểm. Nguyên nhân là trước đây họ ràng buộc Claude quá mức: các chỉ dẫn trong system prompt, skill và yêu cầu của người dùng thường mâu thuẫn nhau — chẳng hạn vừa bảo “viết tài liệu khi cần” vừa bảo “tuyệt đối không viết chú thích” — khiến mô hình phải mất công cân nhắc trước khi quyết định.
Bài viết đối chiếu loạt thói quen cũ với cách làm mới. Thay vì đặt ra luật cứng, hãy để mô hình tự phán đoán — câu chỉ dẫn mới về chú thích chỉ đơn giản là “viết mã nguồn đọc giống mã nguồn xung quanh”. Thay vì cho ví dụ sử dụng công cụ, hãy thiết kế giao diện công cụ cho biểu đạt hơn, vì ví dụ vô tình bó hẹp không gian khám phá. Thay vì dồn hết mọi thứ lên đầu, hãy dùng tiết lộ dần: tách phần kiểm chứng và rà soát mã nguồn thành skill riêng, thậm chí để công cụ nạp trễ và chỉ tìm khi cần. Bộ nhớ nay được lưu tự động thay vì phải tự ghi vào CLAUDE.md, và đặc tả có thể là hiện vật HTML, bộ kiểm thử hay chính mã nguồn thay vì một tệp markdown đơn giản. Lời khuyên cuối: giữ CLAUDE.md nhẹ, dành phần lớn dung lượng cho những điểm dễ vấp trong kho mã nguồn thay vì những điều Claude tự nhìn ra được.
The git history command deserves more attention
Làm việc với nhiều thay đổi song song trong Git vốn khá vất vả: bạn phải nhảy qua lại giữa các nhánh và thực hiện những thao tác rebase đầy rủi ro. Đó là lý do jj (Jujutsu) hay được nhắc tới như một lựa chọn thay thế. Tác giả cho rằng lệnh git history mang lại phần lớn lợi ích tương tự mà không cần rời khỏi hệ sinh thái Git quen thuộc, nhưng lại ít được chú ý. Lệnh con fixup cho phép gấp phần thay đổi đã đưa vào vùng chờ vào một commit cũ rồi tự động rebase mọi nhánh hậu duệ — khác với quy trình autosquash thông thường ở chỗ nó cập nhật tất cả nhánh cục bộ nằm dưới commit được sửa. Lệnh reword sửa thông điệp của một commit cũ và dựng lại cả chồng commit phụ thuộc; vì chỉ chạm vào thông điệp chứ không đụng tới cây làm việc, bạn có thể sửa commit ở nhánh khác mà không làm gián đoạn việc đang làm. Còn split chia một commit thành hai theo kiểu tương tác, chọn từng khối thay đổi, giúp tách các thay đổi không liên quan mà không phải xoay xở với rebase.
Tác giả cũng thẳng thắn về giới hạn: git history chưa xóa hết khoảng cách với jj. Nó không xử lý được xung đột — thay vì coi xung đột là đối tượng hạng nhất như jj, nó từ chối luôn thao tác nào sinh ra xung đột, và fixup cũng không làm việc được với commit gộp. Bù lại, cách tiếp cận này giữ được tính nguyên tử: thao tác hoặc thành công trọn vẹn hoặc không diễn ra. Tài liệu của Git cho thấy giới hạn về xung đột có thể được gỡ bỏ trong tương lai, và với tác giả thì đây đã là một bước tiến đáng kể — mang những lợi ích chính khiến người ta tìm đến công cụ quản lý phiên bản khác vào thẳng bản phân phối lõi của Git.