13/10/2026 · VietTechies · Biên tập

Vào quãng tháng 9 năm 2007, mỗi lần Rob Pike biên dịch code C++ ở Google mất khoảng 45 phút, và khi một lần biên dịch như thế còn chưa chạy xong, ông cùng hai đồng nghiệp đã quyết định bắt tay vào công việc mà về sau cho ra đời ngôn ngữ lập trình Go.
Theo cách Pike mô tả về sau, khi đó ông đang làm “một phần việc nhỏ nhưng then chốt trong một chương trình C++ khổng lồ của Google”. Ngay cả khi chạy trên “cụm máy biên dịch phân tán đồ sộ” của Google, mỗi lần biên dịch của ông vẫn mất ngần ấy thời gian.
Cũng trong thời gian ấy, ông đi nghe một buổi nói chuyện của vài nhân viên Google đang là thành viên ủy ban tiêu chuẩn C++. Họ giới thiệu C++0x, phiên bản kế tiếp của ngôn ngữ này, về sau mang tên C++11. “Trong vòng một tiếng đồng hồ ở buổi nói chuyện ấy”, Pike nhớ lại, “chúng tôi được nghe về chừng 35 tính năng mới đang nằm trong kế hoạch.”
Ông ra về với một câu hỏi. Phải chăng ủy ban thật sự tin rằng vấn đề của C++ nằm ở chỗ ngôn ngữ này chưa có đủ tính năng?
Về lại văn phòng, theo lời chính Pike kể, “tôi cho chạy thêm một lần biên dịch, xoay ghế lại đối diện Robert và bắt đầu đặt ra những câu hỏi sắc bén”. Robert ở đây là Robert Griesemer. “Trước khi lần biên dịch ấy xong”, Pike viết, “chúng tôi đã kéo Ken vào cuộc và quyết định sẽ làm một điều gì đó.” Còn Ken là Ken Thompson. Trên một tấm bảng trắng, ba người cùng viết ra danh sách những thứ họ mong muốn.
Câu chuyện ra đời của Go thường bắt đầu từ lần biên dịch ấy. Thứ họ có được lúc đó mới chỉ là một quyết định, chưa phải một bản thiết kế.
Vì sao 4,2 MB code C++ phình thành 8 GB đầu vào cho trình biên dịch
Trong bài nói chuyện “Go at Google” năm 2012, Pike mô tả một phép đo cho thấy vì sao những lần build như thế lại chậm. Năm 2007, các kỹ sư phụ trách build ở Google đã gắn công cụ đo vào quá trình biên dịch một chương trình lớn của công ty. Khoảng 2.000 file của chương trình này, nếu chỉ nối đuôi nhau, có tổng dung lượng 4,2 megabyte. Đến khi các dòng #include được mở rộng hết, trình biên dịch (compiler) phải nhận hơn 8 gigabyte dữ liệu, tức là “mỗi byte mã nguồn C++ phình ra thành 2.000 byte”. Lần build ấy cũng mất 45 phút, chạy trên một phiên bản tiền thân của hệ thống build phân tán ở Google.
Trong C hay C++, một dòng #include sẽ dán nguyên văn nội dung một file header vào file đang được biên dịch, và các header này lại kéo theo những header khác. Với một chương trình lớn, compiler rốt cuộc phải đọc đi đọc lại những khai báo y hệt nhau.
Những người thiết kế Go coi sự phình to này là vấn đề của ngôn ngữ, chứ không chỉ là chuyện của cụm máy build. Trong Go, import một gói (package) mà không dùng đến sẽ bị báo lỗi ngay lúc biên dịch, nên chương trình không thể lặng lẽ giữ lại những thư viện phụ thuộc (dependency) đã hết cần thiết. Trình biên dịch Go còn ghi dữ liệu export của mỗi gói, tức những gì phần mã dùng gói ấy cần biết, vào ngay file object của gói. Vì dữ liệu này chứa cả thông tin kiểu lấy từ các thư viện phụ thuộc của chính gói đó, mỗi lần import chỉ cần mở đúng một file. Về sau, khi nhóm phát triển Go đo cùng tỷ lệ phình này trên một chương trình lớn của Google viết bằng Go, kết quả là khoảng 40 trên 1, mức mà Pike gọi là “tốt hơn C++ năm mươi lần”.
Họ thực sự đã làm gì sau buổi phác thảo trên bảng trắng
Theo trang Câu hỏi thường gặp (FAQ) của Go, buổi làm việc bên bảng trắng diễn ra vào ngày 21 tháng 9 năm 2007, khi Griesemer, Pike và Thompson “bắt đầu phác thảo các mục tiêu cho một ngôn ngữ mới”. Trong vòng vài ngày, các mục tiêu ấy đã định hình thành một kế hoạch. Sau đó, họ tiếp tục thiết kế bán thời gian, song song với những việc khác.
Đến tháng 1 năm 2008, Thompson đã bắt đầu viết một compiler thử nghiệm sinh ra code C. Tới giữa năm 2008, Go đã trở thành dự án toàn thời gian, và cuối năm đó Russ Cox gia nhập. Ngày 10 tháng 11 năm 2009, Go trở thành dự án mã nguồn mở công khai. Tiếp đó, ngày 28 tháng 3 năm 2012, Go 1 ra mắt cùng lời cam kết rằng các chương trình viết cho Go 1 sẽ “tiếp tục biên dịch và chạy được mà không cần sửa đổi”.
Các mốc thời gian ấy giúp ta nhìn lần biên dịch nổi tiếng kia cho đúng mức. Trong “Go at Google”, Pike viết rằng “theo giai thoại về nguồn gốc của Go, ý tưởng về ngôn ngữ này nảy ra trong một lần build 45 phút như thế”, và lời ông kể về lần biên dịch đó phần lớn khớp với giai thoại này. Quyết định được đưa ra gọn trong một lần biên dịch. Còn để biến quyết định ấy thành một ngôn ngữ được phát hành, họ mất thêm khoảng hai năm.
Phần lớn lập trình viên Go thời đầu đến từ những ngôn ngữ như Python và Ruby, không phải C++
Tháng 6 năm 2012, Pike kể với một nhóm người dùng Go ở San Francisco rằng gần đây có người hỏi ông điều gì bất ngờ nhất khi đưa Go ra mắt. Ông biết ngay câu trả lời: “Dù chúng tôi từng nghĩ lập trình viên C++ sẽ xem Go là một lựa chọn thay thế, phần lớn lập trình viên Go lại đến từ những ngôn ngữ như Python và Ruby. Rất ít người đến từ C++.”
Pike đưa ra một lời giải thích mà chính ông thừa nhận là đơn giản hóa quá mức: mọi chuyện quy về việc mỗi nhóm sẽ phải từ bỏ những gì. Theo ông, lập trình viên Python và Ruby “không phải từ bỏ nhiều khả năng biểu đạt, lại còn có thêm hiệu năng và được vọc xử lý đồng thời (concurrency)”. Còn lập trình viên C++, theo cách ông nhìn nhận, đã “đấu tranh rất vất vả mới giành được quyền kiểm soát đến từng li từng tí trên địa hạt lập trình của mình, và không muốn từ bỏ chút nào”.
Trang FAQ của Go cũng mô tả đúng sự phân hóa này. Theo FAQ, với các ngôn ngữ phổ biến khi ấy, người ta phải chọn giữa biên dịch hiệu quả, thực thi hiệu quả và dễ lập trình, còn lập trình viên nào có thể thì đang “đặt sự dễ dàng lên trên sự an toàn và hiệu quả, bằng cách chuyển sang những ngôn ngữ định kiểu động như Python và JavaScript thay vì C++”.
Pike khép lại buổi nói chuyện ở San Francisco bằng những lời thẳng thừng: “Những người hào hứng với các tính năng mới của C++11 sẽ chẳng quan tâm đến một ngôn ngữ có ít hơn thế rất nhiều. Kể cả khi rốt cuộc ngôn ngữ ấy mang lại nhiều hơn thế rất nhiều.”
Ít tính năng hơn có cái giá của nó, và Go đã phải trả
Go ra mắt mà không có kiểu tổng quát (generics). Phải đến Go 1.18, phát hành tháng 3 năm 2022, generics mới xuất hiện. Khi đó, nhóm phát triển Go viết rằng “hỗ trợ generics lâu nay vẫn là tính năng được yêu cầu nhiều nhất của Go”, đồng thời cho biết bản phát hành này mang theo “thay đổi lớn nhất mà chúng tôi từng thực hiện với ngôn ngữ”.
Dù vậy, Go vẫn tìm được người dùng của mình. Nhân dịp Go tròn mười năm vào năm 2019, Russ Cox kể tên Docker, Etcd, Istio, Kubernetes, Prometheus và Terraform trong số các dự án viết bằng Go, và viết rằng “ngày nay, mọi nhà cung cấp dịch vụ đám mây lớn đều dùng hạ tầng đám mây cốt lõi viết bằng Go”. Trang FAQ giờ đây nói đến hàng triệu lập trình viên Go.
Con số đáng giá là 2.000, không phải 45
Để kể chuyện thì lần biên dịch 45 phút hấp dẫn hơn. Còn để rút ra bài học thì mức phình 2.000 trên 1 có ích hơn. Khi quy trình build nạp cho compiler 2.000 byte ứng với mỗi byte lập trình viên viết ra, vấn đề không chỉ nằm ở phần cứng. Mức phình ấy bắt nguồn từ cách ngôn ngữ cho phép các file phụ thuộc vào nhau. Trước khi thêm máy cho một quy trình build chậm, hãy đo xem compiler thực sự đọc những gì.
Bài học thứ hai là về đối tượng người dùng. Ngay cả những người tạo ra Go cũng đoán sai ai sẽ chuyển sang Go trước tiên. Phần lớn lập trình viên tìm đến Go không phải là những người từng đấu tranh để giành quyền kiểm soát, mà là những người từng đánh đổi tốc độ lấy sự tiện lợi và muốn lấy lại phần nào tốc độ ấy.
Câu hỏi mà Pike mang về từ buổi nói chuyện về C++0x đến nay vẫn còn nguyên giá trị với bất kỳ hệ thống nào bạn đang bảo trì: liệu vấn đề của hệ thống ấy có thật sự nằm ở chỗ chưa có đủ tính năng?
Bài liên quan
Đọc bằng ngôn ngữ khác: English
Nguồn
- Rob Pike, “Less is exponentially more”, nội dung bài nói chuyện tại buổi gặp mặt Go SF ở San Francisco, tháng 6 năm 2012
- The Go Programming Language, “Frequently Asked Questions (FAQ)”: lịch sử dự án và lý do Go ra đời
- Rob Pike, “Go at Google: Language Design in the Service of Software Engineering”, hội nghị SPLASH 2012
- Google Open Source Blog, “Hey! Ho! Let’s Go!” (ngày 10 tháng 11 năm 2009)
- Andrew Gerrand, “Go version 1 is released”, The Go Blog (ngày 28 tháng 3 năm 2012)
- Nhóm phát triển Go (The Go Team), “Go 1.18 is released!”, The Go Blog (ngày 15 tháng 3 năm 2022)
- Russ Cox, “Go Turns 10”, The Go Blog (ngày 8 tháng 11 năm 2019)
- Ảnh đại diện: Rob Pike mặc áo phông in hình chú gopher, linh vật của Go, tại buổi gặp mặt Sydney Google Tech User Group ngày 17 tháng 2 năm 2010 (đã cắt). Tác giả: Christopher Neugebauer, qua Wikimedia Commons, CC BY-SA 2.0
