5 điểm bởi GN⁺ 2026-04-25 | 1 bình luận | Chia sẻ qua WhatsApp
  • Dự án rất dễ rẽ theo hai hướng: làm ngay rồi hoàn thành hoặc để việc khảo sát và thiết kế phình to đến mức đánh mất vấn đề ban đầu; và trong thực tế, phía cứ bắt tay làm thử thường tiến xa hơn
  • Ngay cả khi làm tính năng tìm kiếm fuzzy path cho Emacs, các tính năng phụ của một thư viện tốt cũng có thể tạo ra yêu cầu mới khiến thiết kế phình to; cuối cùng tác giả vứt bỏ toàn bộ phần mã cho tính năng anchor không cần thiết và một lần nữa xác nhận YAGNI
  • Trong diff mã nguồn, so sánh theo dòng không nắm bắt tốt các cấu trúc cấp cao như hàm hay kiểu dữ liệu; ngay cả công cụ dựa trên treesitter cũng có thể hiển thị dài dòng theo kiểu xóa rồi thêm mới nếu việc khớp thực thể bị lệch, khiến khó đọc
  • Hướng cần thiết là trước hết tạo ra một công cụ phạm vi tối thiểu phù hợp với việc review đầu ra LLM theo từng lượt; bắt đầu bằng trích xuất thực thể cho Rust và khớp đơn giản để ưu tiên workflow xem nhanh tổng quan thay đổi ở mức cao

Suy nghĩ quá mức và mở rộng phạm vi

  • Dự án rất dễ phân nhánh thành luồng làm ngay cho xong và luồng đào sâu các ví dụ đi trước đến mức phạm vi cứ lớn dần rồi rốt cuộc lại không giải được bài toán ban đầu
  • Chiếc kệ bếp làm vào cuối tuần được hoàn thành ngay trong cuối tuần sau khi vừa uống cà phê vừa chốt thiết kế, chỉnh lại móc treo in 3D vài lần, rồi dùng vật liệu và sơn còn dư
    • CAD cho móc treo thùng Ikea được công khai trên OnShape CAD
    • Vật liệu được tái sử dụng từ phần dư của bàn làm việc, còn các góc được chà nhám bằng palm sander theo cảm tính
  • Với chiếc kệ này, tiêu chí thành công chính không phải là tạo ra món đồ khớp hoàn hảo với gian bếp, mà là tận hưởng việc làm mộc cùng bạn bè; nhờ vậy bớt phải đắn đo quá mức về các tiêu chí chi tiết
  • Ngược lại, trong quá trình tìm một công cụ diff có cấu trúc, tác giả thấy kết quả của difftastic chưa thỏa mãn nên đã dành 4 tiếng khảo sát các công cụ và workflow liên quan, rồi cuối cùng vẫn quay về tiêu chí ban đầu là một workflow diff tốt hơn để dùng trong Emacs
  • Những mối quan tâm lâu năm như giao diện cho prototyping phần cứng, một ngôn ngữ trộn Clojure với Rust, hay một ngôn ngữ cho CAD đều đã ngốn hàng trăm giờ nghiên cứu nền và làm prototype nhỏ, nhưng vẫn chưa dẫn tới một thành phẩm trực tiếp giải quyết động cơ ban đầu
  • Với các dự án ngôn ngữ và CAD, tiêu chí thành công bị mơ hồ: liệu chúng có thay thế Rust hay Clojure không, hay chỉ xử lý một phần vấn đề, liệu một sân chơi để học tập là đủ, có thay được CAD thương mại không, hay có cần hữu ích với cả người khác không
  • Việc cân nhắc những câu hỏi này có giá trị, nhưng tác giả cho rằng thay vì chỉ xem xét thật nhiều thứ thì tốt hơn là thực sự làm ra thật nhiều thứ
  • Dù nhìn lại sau này có thể thấy kết quả rõ ràng là chưa tốt, phía cứ làm thử vẫn giúp tiến xa hơn về tổng thể

Định luật bảo toàn của mở rộng phạm vi

  • Thời gian cứ lao vào làm cũng có giới hạn và cần cân bằng; trải nghiệm để LLM agent viết rất nhiều mã rồi cuối cùng bỏ hết lại khiến tác giả nhớ tới YAGNI
  • Tác giả muốn làm một tính năng tìm kiếm fuzzy path toàn filesystem kiểu Finda để dùng trong Emacs, và vì trước đây từng tự viết tay tính năng tương tự nên nghĩ rằng chỉ cần giám sát LLM là có thể xong trong vài giờ
  • Ban đầu, trong cuộc trao đổi để lên kế hoạch, tác giả được gợi ý dùng Nucleo; vì nó được thiết kế và tài liệu hóa tốt nên đã chọn để có các tính năng smart caseUnicode normalization
    • Ví dụ, truy vấn foo sẽ khớp cả Foofoo, nhưng Foo thì không khớp foo
    • Cách xử lý cafecafé cũng nằm trong cùng ngữ cảnh
  • Vấn đề không nằm ở bản thân thư viện tốt, mà ở chỗ Nucleo còn hỗ trợ tính năng anchor
  • Với một corpus chỉ gồm đường dẫn file, anchor đầu dòng có vẻ không hữu ích, nên tác giả định diễn giải nó thành anchor theo path segment
    • Ví dụ, ^foo sẽ khớp /root/foobar/ nhưng không khớp /root/barfoo/
  • Để xử lý hiệu quả điều này, index phải lưu biên segment và phải có khả năng kiểm tra truy vấn nhanh cho từng segment
  • Sau đó còn phải xử lý cả truy vấn anchor có dấu gạch chéo như ^foo/bar, và chỉ kiểm tra theo từng segment thì trở nên khó khớp đúng những đường dẫn như /root/foo/bar/baz/
  • Tác giả lại dành thêm vài giờ cho thiết kế này, trao đổi ý tưởng với LLM, viết mã bọc quanh các kiểu của Nucleo, rồi vì mã trở nên quá cồng kềnh và không vừa ý nên cuối cùng đã tự viết lại một wrapper nhỏ hơn
  • Sau khi nghỉ một lúc, tác giả nhận ra không nhớ lần nào thật sự cần tính năng anchor trong Finda, và với corpus đường dẫn thì chỉ cần thêm / ở đầu hoặc cuối truy vấn là đã thay thế được phần lớn vai trò của anchor
    • Ngoại lệ duy nhất còn lại là anchor cho cuối tên file
  • Cuối cùng toàn bộ mã liên quan đến anchor bị vứt bỏ, và tác giả cũng không chắc việc dùng LLM và thảo luận với người khác có còn lợi hơn so với tự viết trực tiếp từ đầu hay không
  • Có vẻ như khi tốc độ lập trình tăng lên thì cũng tồn tại một thứ giống định luật bảo toàn khiến tính năng không cần thiết, rabbit holeđường vòng tăng theo tương ứng

Diff có cấu trúc

  • Trong mã nguồn, diff thường có nghĩa là bản tóm tắt thay đổi theo dòng giữa hai phiên bản của một file; ở unified view, phần thêm và xóa được ký hiệu bằng +, -
  • Cùng một diff cũng có thể được render theo dạng so sánh trái-phải, và khi thay đổi phức tạp hơn thì kiểu hiển thị này có thể dễ đọc hơn
  • Vấn đề của diff theo dòng là nó không nhận biết được các cấu trúc cấp cao như hàm hay kiểu dữ liệu; nếu dấu ngoặc nhọn tình cờ vẫn khớp thì phần hiển thị thậm chí có thể bị lược bỏ dù chúng thuộc về các hàm khác nhau
  • difftastic cố giảm bớt vấn đề này bằng cách dùng concrete syntax tree do treesitter cung cấp, nhưng việc khớp thực thể giữa các phiên bản không phải lúc nào cũng tốt
  • Trong diff đã trực tiếp khơi mào chuyện này, struct PendingClick ở hai bên không được xem là tương ứng với nhau, nên bên trái bị hiển thị như đã xóa còn bên phải như mới được thêm vào
  • Tác giả không đào sâu lý do vì sao việc khớp thất bại, nhưng cho rằng dù toàn bộ diff có dài hơn thì vẫn tốt hơn nếu PendingClickRequestPendingClick được xem là tương ứng ở cả hai phía

Công cụ diff có cấu trúc và tài liệu tham khảo

  • Công cụ semantic diff hoàn thiện và được trau chuốt cẩn thận nhất mà tác giả đánh giá cao là semanticdiff.com
    • Nó do một công ty nhỏ ở Đức cung cấp, có plugin VSCode miễn phí và web app để xem GitHub PR diff
    • Tuy nhiên, họ không cung cấp thư viện mã có thể dùng làm nền cho workflow mà tác giả mong muốn
    • Bài semanticdiff vs. difftastic có rất nhiều chi tiết hữu ích, bao gồm cả vấn đề difftastic không thể hiện được ngay cả thay đổi thụt lề có ý nghĩa trong Python
    • Một trong các tác giả nói trong bình luận HN rằng họ đã rời khỏi hướng dùng treesitter để xử lý ngữ nghĩa; do keyword phụ thuộc ngữ cảnh và hành vi của lexer nên việc parse có thể thất bại, đến mức công cụ có thể dừng lại ngay cả khi một tên như async được dùng làm tham số
  • diffsitter là công cụ dựa trên treesitter và có kèm MCP server
    • Dù có nhiều GitHub star, tài liệu của nó trông không mấy tốt và cũng khó tìm tài liệu giải thích cách nó hoạt động
    • Wiki của difftastic viết rằng nó thực hiện longest-common-subsequence trên các leaf của cây
  • gumtree là công cụ xuất phát từ bối cảnh nghiên cứu học thuật năm 2014
    • Vì cần Java nên nó không hợp với nhu cầu cá nhân là một công cụ dùng nhanh trong Emacs
  • mergiraf là một merge-driver khác dựa trên treesitter, viết bằng Rust
    • Architecture overview được trình bày rất tốt và bên trong sử dụng thuật toán Gumtree
    • Tài liệu và hình minh họa tạo cảm giác đây là một dự án được làm rất chỉn chu
    • Tác giả semanticdiff.com viết trong bình luận HN rằng GumTree cho kết quả nhanh, nhưng ngay cả khi áp dụng các cải tiến từ nhiều bài báo tiếp theo thì vẫn khá thường xuyên trả về khớp nối tệ, nên cuối cùng họ chuyển sang cách tiếp cận dựa trên dijkstra để tối thiểu hóa chi phí mapping
  • weave là một merge-driver dựa trên treesitter khác, cũng viết bằng Rust
    • Landing page hào nhoáng, nhiều GitHub star, có MCP server, nên ấn tượng tổng thể hơi phô trương
    • Tác giả đã xem crate trích xuất thực thể sem
    • Phần mã diff cốt lõi ổn nhưng hơi dài dòng, còn việc khớp thực thể dùng thuật toán greedy
    • Mô hình dữ liệu không phát hiện được việc di chuyển bên trong file, trong khi kiểu di chuyển đó có thể mang ý nghĩa lớn
    • Nó cũng chứa khá nhiều impact analysis dựa trên heuristic, thứ có vẻ cần tích hợp ngôn ngữ mạnh hơn nếu muốn đáng tin
      • Khi chạy sem diff --verbose HEAD~4, tác giả còn gặp đầu ra lỗi hiển thị những dòng không hề thay đổi như thể đã thay đổi
    • Có quá nhiều tính năng giả định hữu ích ở trạng thái như mới hoàn thành khoảng 80%, nên không phù hợp để dùng làm nền, dù tác giả vẫn đánh giá cao việc họ làm được đến mức này chỉ trong 3 tháng
  • diffast tính tree edit-distance của AST dựa trên thuật toán từ một bài báo học thuật năm 2008
    • Nó hỗ trợ Python, Java, Verilog, Fortran, C/C++ thông qua parser chuyên dụng
    • Example AST differences gallery được tổ chức rất tốt
    • Nó có thể xuất thông tin theo dạng tuple để dùng với datalog
  • autochrome là công cụ diff dành riêng cho Clojure và dùng dynamic programming
    • Phần giải thích trực quan và walkthrough ví dụ rất xuất sắc
  • Bài Designing a Tree Diff Algorithm Using Dynamic Programming and A* của Tristan Hume là tài liệu rất đáng tham khảo về thiết kế thuật toán tree diff

Workflow mong muốn và kế hoạch phạm vi tối thiểu

  • Trường hợp sử dụng chính là review đầu ra LLM theo từng lượt, và tác giả không để agent tạo ào ạt hơn 10.000 dòng mã trong một lần
  • Tác giả giao cho agent các tác vụ có phạm vi rõ ràng, quay lại sau vài phút để xem tổng quan toàn bộ thay đổi, rồi muốn hoặc tự sửa trực tiếp trong Emacs, hoặc bỏ hết để làm lại, hoặc thậm chí tự viết lại từ đầu
  • Workflow mong muốn là trước hết nhìn thấy tổng quan cấp cao về những kiểu, hàm, phương thức nào đã được thêm, xóa hay sửa
  • Trên nền đó, phải có khả năng nhanh chóng bung ra text diff cho từng thực thể, để mở rộng tự nhiên từ tóm tắt sang diff chi tiết
  • Ngoài ra, tác giả còn muốn có thể sửa ngay tại chỗ thay đổi mà không phải chuyển sang màn hình file, tức là chỉnh sửa inline ngay trong màn hình diff
  • Đích nhắm là đưa workflow review thay đổi và staging của Magit từ đơn vị file và dòng sang đơn vị thực thể
  • Phù hợp với bài học vừa được nhớ lại về phạm vi tối thiểu, tác giả dự định trước hết tự gấp rút làm một framework trích xuất thực thể dựa trên treesitter chỉ dành cho Rust
  • Phần matching sẽ bắt đầu bằng cách greedy đơn giản, còn diff sẽ được render ở dòng lệnh
  • Nếu mức này đã cho kết quả tốt hơn difftastic trên commit cụ thể kia, bước tiếp theo sẽ là nối nó vào một workflow Emacs tương tác hơn kiểu Magit
    • Nếu có thể, tác giả cũng để ngỏ khả năng tái sử dụng chính Magit
    • Hỗ trợ ngôn ngữ mới sẽ được thêm dần khi cần
    • Về sau có thể thử khớp toàn cục dựa trên điểm số thay cho greedy đơn giản
  • Nếu đủ hài lòng thì có thể công bố, nhưng mục tiêu không phải là kiếm GitHub star hay HN karma; nó cũng có thể chỉ là một công cụ riêng để âm thầm dùng một mình
  • Câu kết rằng đôi khi người ta chỉ cần đúng một cái kệ thôi đã gói lại thông điệp về làm đúng thứ cần thiết thay vì mở rộng quá đà

1 bình luận

 
GN⁺ 2026-04-25
Ý kiến trên Hacker News
  • Tôi nghĩ bài này cho thấy rất rõ khó khăn lớn nhất của nghiên cứu PhD
    Khi chọn một chủ đề thú vị và đọc càng nhiều công trình liên quan càng tốt, bạn dễ nhận ra đã có rất nhiều người làm điều mình định làm, và từ đó scope creep rất dễ trở nên nghiêm trọng
    Sau khi đã tiêu hết năng lượng và sự hào hứng ban đầu, bạn phải gượng ép đẩy nốt 20~30% còn lại để kéo nó tới trạng thái có thể xuất bản

    • Ngày 1 thì bắt đầu với ý tưởng áp dụng chất xúc tác công nghiệp hiện có vào một mục đích mới để giảm chi phí sản xuất tiền chất cho thuốc thiết yếu
      Đến ngày 400 thì gần như giải thích xong thuyết vạn vật và đang định chế tạo một thiết bị thí nghiệm quỹ đạo tại điểm Lagrange để phát hiện hạt phổ quát trung gian cho mọi lực trong vũ trụ đã biết
    • Cảm giác đó thật sự quá chân thực
      Tôi muốn biết phải làm gì để giảm bớt chuyện này
    • Tôi nghĩ phần lớn nghiên cứu sinh tiến sĩ đều trải qua điều này, vì mục đích của PhD rốt cuộc là chứng minh rằng bạn có thể làm normal science
      Trên thực tế, nó gần giống kiểu nâng khả năng quan sát của một hệ thống từ 1% lên 1.001%, và là một cánh cổng để bước vào con đường học thuật
      Vì vậy tôi hiếm khi thấy luận án nào thực sự thú vị, cực kỳ mới mẻ, hoặc có thể áp dụng trực tiếp vào khoa học
    • Hơn nữa còn phải chịu đựng chuyện này trong trạng thái ngày càng nặng nề vì hối hận đã bắt đầu PhD ngay từ đầu
    • Bắt đầu bằng cách cố đọc toàn bộ công trình trước đó rõ ràng là một cách tiếp cận sai
      Thực tế tôi gần như không thấy ai nghiên cứu như vậy; thông thường đọc khoảng hai ba bài rồi xây tiếp từ đó mới là hợp lý
      Việc rà sâu tài liệu nghiên cứu nên làm sau khi đã có chút kết quả và bắt đầu viết lại thì tốt hơn
  • Tôi cứ nghĩ mãi về câu nói rằng chỉ cần tốt hơn là đủ
    Những cải tiến nhỏ cũng sẽ tích lũy theo thời gian, và vì ngay từ đầu chẳng có gì là hoàn toàn mới, nên ngồi đó cố vắt ra một thiết kế hoàn hảo lại dễ phản tác dụng
    Câu “chướng ngại vật chính là con đường” cũng rất hợp ở đây

    • Câu đừng để sự hoàn hảo trở thành kẻ thù của thứ đủ tốt thật sự rất đúng
      Một đồng nghiệp từng làm cùng tôi, khi phê bình thay đổi trong code, nếu cảm thấy mình đang bắt lỗi quá vụn vặt thì sẽ nói “vẫn tốt hơn trước”
      Nhờ vậy, anh ấy vừa chỉ ra được điểm cần cải thiện, vừa đồng thời cho phép mọi người tiếp tục tiến lên dù vẫn còn vài lỗi nhỏ, và tôi rất ủng hộ thái độ này
    • Suy cho cùng thì đây là chủ nghĩa hoàn hảo
      Trước đây tôi chỉ nghĩ chủ nghĩa hoàn hảo là cố gắng đạt thành tích quá cao một cách quá sức, nhưng việc không chấp nhận thứ gì không hoàn hảo rồi bỏ cuộc mà chẳng tiến triển gì cũng có thể là hoàn hảo chủ nghĩa
      Sự trì hoãn với những việc lớn cũng thường có cùng một gốc rễ
  • Tôi thích điều CEO của Rec Room từng nói
    Các team lúc nào cũng ước dự án ngắn hơn, chứ hiếm khi nói rằng lẽ ra nên dời phát hành thêm, làm nó phức tạp hơn và trau chuốt hơn nữa
    Dù không đúng 100% trong mọi trường hợp, nhưng nếu đã phải mắc sai lầm thì tôi nghĩ làm nhỏ và phát hành sớm vẫn tốt hơn là làm quá lớn rồi lãng phí thời gian

  • Con người vốn dễ nảy ra những ý tưởng giống nhau, nên nếu cứ hoàn thành dự án mà không biết trước thì cuối cùng nó cũng dễ trở thành một dạng tái phát minh nào đó
    Ngược lại, nếu nghiên cứu trước, bạn có thể nhận ra rằng một phần nào đó chỉ là lặp lại thứ đã tồn tại và bị mất hứng
    Dù vậy, việc tự mình làm tới cùng để học hỏi vẫn có thể là điều quan trọng nhất
    Tất nhiên, nếu bạn phải tạo ra một đóng góp học thuật mới hoặc kiếm tiền từ một dự án mang tính độc nhất thì sẽ khó hơn, nhưng ngay cả trong những lĩnh vực đó người ta cũng bất ngờ khoan dung với việc chỉ cần bẻ hướng nhẹ từ cái có sẵn

  • Tôi đang gặp đúng tình huống này với side project của mình
    Lĩnh vực là Information Retrieval nên tôi còn ít kinh nghiệm, và đương nhiên có rất nhiều prior art để học hoặc tích hợp
    Vì vậy sau khi đọc bài này, tôi thấy nghiêng hơn về hướng cứ làm cái của mình trước, chỉ nhìn vào các ví dụ có sẵn khi bị bí hoặc cần ý tưởng
    Mặt khác, nếu xem phim tài liệu Clojure ra gần đây thì Rich Hickey dường như lại đào rất sâu vào prior art, bài báo và các ngôn ngữ khác trong thời gian dài rồi mới bắt tay vào làm
    Nhưng bản thân ông ấy trước đó cũng đã tạo ra các ngôn ngữ khác rồi, nên bức tranh lớn vẫn là bắt đầu từ việc tự làm để học
    Có lẽ không nên chỉ suy nghĩ quá lâu; cứ làm trước, tích lũy bài học từ thực tế, va vào tường rồi lúc đó mới cần nghiên cứu sâu hơn

  • Việc đặt deadline đã giải quyết phần lớn vấn đề scope creep cho tôi
    Theo cảm nhận của tôi, những dự án có hạn chót cứng như game jam hay cuộc thi lập trình thì dễ hoàn thành hơn, còn những dự án mở vô thời hạn thì khó đi đến đích hơn nhiều
    Tôi thấy điều này cũng giống như việc chuẩn C++ được phát hành mỗi 3 năm thay vì chờ cho đến khi mọi tính năng mong muốn đều sẵn sàng
    https://news.ycombinator.com/item?id=20428703

  • Bài viết khá thú vị, nhưng tôi thấy suy nghĩ của tác giả có phần lan man, tản mát

    • Ở đây cốt lõi rốt cuộc vẫn là scope creep
    • Đây không phải là một bài blog đào sâu sắc bén vào một chủ đề cụ thể, mà gần hơn với một bản cập nhật newsletter gửi cho những người theo dõi tác giả
  • Với một người nói rằng mình bị scope creep áp đảo, tác giả lại có vẻ là kiểu làm được cực nhiều việc, đến mức cuối bài còn gắn cả đống link về đủ loại chủ đề
    Cuối cùng có vẻ đây là kiểu người thực sự thích học và thử nhiều thứ, và chính quá trình sa vào rabbit hole mới là thứ kích thích đầu óc họ một cách thú vị

  • Với tư cách là người làm một mình, có một nhận ra đã giúp tôi rất nhiều
    Phần lớn những thứ trông như abstraction bắt buộc thật ra chỉ là scope creep đổi tên
    Sau khi cứ gắn flag cho từng tính năng mới và thấy một pattern trong code của mình, tôi đặt ra một quy tắc
    Đó là không phát hành tính năng nào nếu không có test cho hành vi khi flag-off
    Từ đó tôi bắt đầu nhìn flag không phải như lối thoát mà là một phần của sản phẩm, và ba tính năng nằm trong backlog đã tự nhiên biến mất khi tôi bắt đầu nghĩ như vậy

  • Đúng là lên kế hoạch quá mức và scope creep là vấn đề, nhưng ngược lại cũng cần cảnh giác với việc nghiêng quá xa sang phía phát triển ngẫu hứng
    Một số dự án thành công nhất của tôi thực ra lại là những trường hợp tôi đã lên kế hoạch và rà soát trước hầu hết tính năng bằng cách mô hình hóa dữ liệu trước khi tạo ra phần mềm chạy được
    Ở giai đoạn đó thường rất khó biết cái gì là quá tay, và nếu bỏ đi những tính năng mà tôi hoặc người dùng có thể muốn thì sau này sẽ tốn rất nhiều thời gian để thiết kế lại phần lõi của code
    Ngược lại, nếu đoán sai theo hướng kia thì dự án lại phình ra quá lớn và ta gọi đó là scope creep
    Cuối cùng thì phán đoán này phụ thuộc vào việc bạn hiểu domain đến mức nào
    Nếu bạn biết ít hơn mình tưởng thì sẽ phải làm lại rất nhiều, còn nếu bạn biết nhiều hơn mình tưởng thì thực ra đã có thể đi lớn hơn nhưng lại lãng phí thời gian với baby step
    Dù đi hướng nào cũng vẫn để lại tiếc nuối, nên rốt cuộc tôi thấy đây là một vấn đề phán đoán rất lớn

    • Cách lý tưởng, theo tôi, là dành đủ thời gian ở giai đoạn phân tích để nạp đúng ngữ cảnh vào đầu, nhưng khi bắt tay vào làm thì sẵn sàng bỏ những giải pháp over-engineered và triển khai ngay theo hướng thuận tay nhất
      Không nên mắc ngụy biện chi phí chìm, và việc bỏ vài giờ nghiên cứu một chủ đề cỡ PhD không có nghĩa là bạn nhất định phải dùng nó trong dự án
      Nếu nó không thực sự khớp với vấn đề hiện tại thì nên mạnh dạn bỏ đi
    • Đừng quá lo mình sẽ sai, cứ làm thử trước rồi nếu cần thì điều chỉnh sau