- Trong Go 1.26, lệnh
go fix được viết lại hoàn toàn đã được giới thiệu, cho phép tự động cải thiện mã để tận dụng các tính năng mới nhất của ngôn ngữ và thư viện
- Công cụ này phát hiện các mẫu mã thông qua hàng chục analyzer và áp dụng nhiều modernizer như
minmax, rangeint, stringscut để chuyển đổi mã lặp lại hoặc lỗi thời sang dạng hiện đại hơn
- Để hỗ trợ tính năng mới
new(expr), analyzer newexpr đã được thêm vào, có thể tự động đơn giản hóa các hàm helper như newInt
go fix tạo ra hiệu ứng cộng hưởng khi chạy nhiều lần; các analyzer khác nhau có thể liên tiếp đề xuất cải tiến, đồng thời bao gồm khả năng tự động hợp nhất khi xung đột và loại bỏ các import không cần thiết
- Nhóm Go dự định trong tương lai sẽ mở rộng theo mô hình phân tích “Self-service” để các nhà phát triển có thể tự định nghĩa và phân phối modernizer cho API của riêng họ
Tổng quan về lệnh go fix
- Trong Go 1.26,
go fix được triển khai lại hoàn toàn, cung cấp khả năng tự động chuyển đổi codebase sang phong cách Go hiện đại
- Có thể sửa tất cả package trong thư mục hiện tại trở xuống bằng lệnh
go fix ./...
- Có thể xem trước thay đổi bằng tùy chọn
-diff
- Có thể xem danh sách analyzer đã đăng ký bằng
go tool fix help, bao gồm nhiều quy tắc chuyển đổi như any, forvar, mapsloop, minmax
- Nếu chỉ muốn chạy một analyzer cụ thể thì dùng cờ như
-any, còn để loại trừ thì chỉ định -any=false
- Có thể chạy nhiều lần theo từng tổ hợp
GOOS, GOARCH để tính đến khác biệt mã theo nền tảng
Modernizers — công cụ hiện đại hóa mã
- Từ Go 1.18 trở đi, việc giới thiệu generics giúp mở rộng đáng kể khả năng đơn giản hóa mã
- Ví dụ: dùng
maps.Keys để thu thập khóa của map, dùng strings.Cut để tách chuỗi
- Để giải quyết vấn đề các công cụ sinh mã dựa trên LLM vẫn giữ các mẫu cũ, bài viết nhấn mạnh sự cần thiết của việc cập nhật mã nguồn mở để phản ánh các idiom Go mới nhất
- Các modernizer có trong
go fix và gopls giúp tăng khả năng đọc hiểu mã và hiệu quả học tập
- Một số modernizer tiêu biểu:
- minmax: thay câu lệnh
if bằng hàm min/max
- rangeint: chuyển vòng lặp
for 3 thành phần sang range-over-int
- stringscut: đơn giản hóa mã dựa trên
strings.Index thành strings.Cut
Tính năng new(expr) trong Go 1.26
- Hàm
new được mở rộng để chấp nhận đối số giá trị, cho phép khởi tạo theo dạng new("go1.26")
- Analyzer
newexpr tìm các hàm helper như newInt và đơn giản hóa thành return new(x), đồng thời thay các chỗ gọi bằng new(expr)
- Chỉ áp dụng khi đáp ứng phiên bản Go tối thiểu, ví dụ chỉ thị
go 1.26
- Có thể áp dụng cho toàn bộ codebase bằng lệnh
$ go fix -newexpr ./...
- Sau khi dùng, các hàm helper không còn cần thiết có thể được nhận diện bằng công cụ
deadcode
Hiệu ứng cộng hưởng và xử lý xung đột
- Có hiệu ứng cộng hưởng khi một lần sửa tạo ra cơ hội cho các lần sửa khác
- Ví dụ: sau khi áp dụng
minmax có thể xuất hiện thêm các đề xuất chuyển đổi khác
- Có thể có chuỗi tối ưu hóa liên tiếp như
stringsbuilder → fmt.Fprintf
go fix tự động hợp nhất xung đột sửa đổi bằng thuật toán 3-way merge
- Nếu có xung đột cú pháp thì bỏ qua thay đổi đó và hiển thị cảnh báo
- Xung đột ngữ nghĩa, ví dụ xóa biến hoặc import không còn dùng, có thể cần điều chỉnh thủ công
- Các import không cần thiết sẽ được tự động loại bỏ
Tích hợp với framework phân tích Go
go vet và go fix được tích hợp để chia sẻ cùng một framework phân tích
vet tập trung vào phát hiện lỗi, còn fix tập trung vào tự động sửa an toàn
- Analyzer có thể chạy trên nhiều driver như
unitchecker, multichecker, gopls, staticcheck, Tricorder
- Hệ thống fact cho phép chia sẻ thông tin giữa các package
- Ví dụ: suy luận rằng
log.Printf là wrapper của fmt.Printf
gopls cung cấp chẩn đoán thời gian thực và gợi ý sửa tự động
Cải tiến hạ tầng phân tích
- Mở rộng gói
inspector giúp tăng hiệu quả duyệt AST, với kiểu Cursor hỗ trợ di chuyển lên/xuống/trái/phải
- typeindex giúp lập chỉ mục lời gọi hàm, tăng tốc độ phân tích lên đến 1000 lần
- Các cải tiến bổ sung:
- Cung cấp đồ thị phụ thuộc của thư viện chuẩn
- Hỗ trợ truy vấn phiên bản Go theo từng tệp
- Mở rộng các primitive refactoring để sửa mã an toàn, như thao tác với chú thích
- Một số modernizer bị loại trừ do có thay đổi hành vi tinh vi (ví dụ
append([]string{}, slice...) → slices.Clone(slice))
- Trong tương lai dự kiến phát triển engine so khớp mẫu, test harness tự động, và thư viện toán tử sửa đổi chính xác
Mô hình Self-service
- Từ Go 1.26 sẽ bắt đầu giới thiệu mô hình phân tích self-service
- Nhà phát triển có thể tự định nghĩa và phân phối modernizer cho API của riêng mình
- Có thể chạy ở cấp dự án mà không cần quy trình phê duyệt tập trung
- Ở giai đoạn đầu, tính năng annotation-driven inliner đã được đưa vào dưới dạng bản xem trước
- Kế hoạch tương lai:
- Chạy analyzer tùy biến thông qua dynamic loading (trong
go fix hoặc gopls)
- Khái quát hóa kiểm tra dựa trên control flow, ví dụ xác minh các bất biến như “open rồi phải close”, “lock rồi phải unlock”
- Mục tiêu là nâng cao hiệu quả bảo trì và hỗ trợ áp dụng nhanh các tính năng Go mới nhất
1 bình luận
Ý kiến trên Hacker News
Vào cuối năm 2024, khi các trợ lý viết mã bằng LLM lan rộng rất nhanh, điều thú vị là các công cụ này có xu hướng tái tạo nguyên trạng phong cách mã Go cũ từ dữ liệu huấn luyện
Ngay cả khi được yêu cầu dùng cú pháp mới nhất, chúng vẫn bỏ qua, thậm chí có lúc còn phủ nhận là nó tồn tại
Để các mô hình tương lai phản ánh được idiom Go 1.25 mới nhất, toàn bộ mã nguồn mở cũng sẽ phải được cập nhật sang phong cách đó
Nhưng với LLM, một khi dữ liệu sai đã lọt vào thì gần như không thể sửa được
Rất khó lần theo cơ sở mà mô hình dùng để đi đến kết luận, và chỉ còn biết hy vọng nó sẽ được sửa trong mô hình thế hệ sau
Nó trông đơn giản nên dễ qua review, nhưng thực tế lại thiếu xử lý lỗi và các trường hợp biên
Sau khi review rồi đưa lại cho LLM, bề ngoài có vẻ mã đã được sửa, nhưng bên trong lại phát sinh data race hoặc deadlock
Đây là vấn đề lặp lại ở gần như mọi mô hình
Go có khả năng tương thích ngược tốt nên vẫn biên dịch được, nhưng phong cách mã thì thay đổi quá nhiều
Với Python, thay đổi API còn gây ra vỡ tương thích thật sự
Dù vậy, nhờ độ ổn định của ngôn ngữ và thư viện chuẩn, Go vẫn là một ngôn ngữ rất xuất sắc để sinh mã
Như Rob Pike đã cảnh báo, công nghệ kiểu này là sự ô nhiễm của hệ sinh thái phần mềm
Nhiều người muốn thứ slop mang lại “sự tiện lợi”, nhưng đó mới chính là cốt lõi của vấn đề
Một công cụ có thể tự động chuyển đổi mã nguồn sang phong cách mới nhất thực sự rất tuyệt
OpenRewrite của Java là ví dụ tiêu biểu, nhưng ở các ngôn ngữ khác thì khó nghĩ ra thứ tương tự
Nếu một khả năng như vậy được tích hợp sẵn trong ngôn ngữ như Go, thì độ trưởng thành của ngôn ngữ tăng lên rất nhiều
Có lẽ các ngôn ngữ mới sau này sẽ tham khảo cách tiếp cận tích hợp kiểu Go
IDE của JetBrains có thể refactor hàng triệu dòng mã cùng lúc hoặc tự động chuyển sang cú pháp mới
Cũng có các tính năng như ConvertToPrimaryConstructor
Ngoài ra, Structural Search and Replace hoạt động ở mức cú pháp ngôn ngữ chứ không chỉ là văn bản thuần
Bộ phân tích Roslyn của .NET cũng đưa ra gợi ý sửa mã ngay trong IDE
Liên kết hướng dẫn
Nhờ vậy mã đã sạch sẽ hơn rất nhiều
Nó có thể đổi
concatvàmapthànhconcatMap, hoặc đơn giản hóa những biểu thứcifkhông cần thiếtMáy chủ LSP thiếu chức năng, thậm chí còn không hỗ trợ các thao tác refactor cơ bản như xóa tham số
Tôi đang cân nhắc liệu có thể kết hợp jscodeshift hay Claude để làm việc đó hay không
Nhờ những công cụ tự động sửa lỗi (go fix) như thế này mà Go thực sự là một ngôn ngữ xuất sắc
Tính năng mới rangeint cũng dự kiến sẽ được áp dụng tự động qua go fix, nên rất đáng mong đợi
Xin gửi lời khen ngợi tới đội ngũ Go
Tốc độ biên dịch cũng nhanh đến mức khó tin
for, còn giờ công cụ này xử lý theo cách tinh tế hơn nhiềuBài viết không nhắc đến, nhưng tính năng tôi thích nhất là chỉ thị
//go:fix inlineNó chèn một hàm chỉ có một dòng inline vào chỗ gọi
Nhờ đó tác giả thư viện có thể tự động migration từ hàm cũ sang hàm mới một cách tự nhiên
Ngay cả khi semver thay đổi, vẫn có thể tự động nâng cấp bằng
go fixTrong podcast của Wes McKinney mà tôi xem gần đây,
ông nói Go rất lý tưởng cho coding agent nhờ chu kỳ compile-run nhanh, hệ thống kiểu mạnh, và độ an toàn đa luồng
Nghe vậy xong tôi lại thấy hứng thú với Go
Hệ thống công cụ và quy ước đã được thiết lập của Go rất hữu ích cho phát triển dựa trên agent
Có thể dựng ngay môi trường phát triển bằng
go run main.go, đồng thời hỗ trợ nhiều worktree, cấu hình trung tâm và cả cơ sở dữ liệu đã migrationhousecat-inc/cheetah có chia sẻ kiểu công cụ này
Tôi đang định thêm
go fixvào vòng lặp nhanh vốn đã cógo generate,go build,go test,go vetKhi học Python, tôi thấy có quá nhiều cách để làm cùng một việc và thiếu nhất quán
Đôi khi lại thấy nhớ kiểu chỉ có một cách như ở C
Tôi muốn biết liệu Go đã đạt tới giai đoạn đó chưa
Nó có phải là ngôn ngữ mà ngay cả không cần trợ giúp từ LLM vẫn có thể làm theo best practice hay không
Điều đáng ấn tượng là nó cố giữ mọi thứ không phức tạp và đơn giản
Tôi khuyên dùng cho những ai đã mệt mỏi với sự hỗn loạn của Python
Khái niệm self-service analyzer thực sự rất thú vị
Có vẻ các đội thư viện lớn hoặc đội hạ tầng sẽ tận dụng nó rất tích cực
Có công cụ như vậy thì khiến tôi nghĩ liệu ngay cả ngôn ngữ không có tương thích ngược cũng có thể khả thi hơn không
Trong thế giới TypeScript, biome đóng vai trò như vậy
Ví dụ, nó khuyến nghị dùng
for...ofthay choforEach, và nếu dùng cùng ultracite thì quy trình làm việc trở nên mượt hơn nhiềuTôi đã ghi rõ trong file
agents.mdlà “sau khi sửa thì chạy biome fix”, và nhờ vậy chất lượng mã được duy trì tự độngĐây là trải nghiệm nhẹ và hiệu quả hơn nhiều so với eslint