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

Khoảng năm 1976 tại Bell Labs, Stuart Feldman viết ra Make, công cụ Unix chỉ build lại những phần chịu ảnh hưởng của một thay đổi, và đặt cho công cụ này một quy tắc về ký tự tab mà đến nay lập trình viên vẫn vấp phải. Về sau, ông kể rằng mình biết quy tắc ấy là một ý tưởng tồi, nhưng vẫn giữ lại, vì trong vòng vài tuần, Make đã có khoảng một tá người dùng.
Make ra đời từ buổi sáng tồi tệ của một người khác. Theo lời Feldman kể, Steve Johnson — tác giả của yacc, công cụ sinh bộ phân tích cú pháp — đã “hầm hầm xông vào phòng tôi, nguyền rủa số phận đã khiến anh ấy phí mất một buổi sáng để gỡ lỗi một chương trình vốn đã đúng”. Johnson đã sửa xong lỗi từ trước. Nhưng file đã sửa chưa được biên dịch lại, nên chương trình mà ông cứ chạy thử đi chạy thử lại vẫn được tạo ra từ code cũ.
Feldman hiểu rõ cảm giác ấy. Ông cũng đã mất một phần buổi tối hôm trước để “đối phó với đúng tai họa đó” trong một dự án của riêng mình. Ông nhớ lại rằng công cụ này “khởi đầu từ một ý tưởng cầu kỳ về một bộ phân tích quan hệ phụ thuộc, rồi được rút gọn thành một thứ đơn giản hơn nhiều, và trở thành Make ngay trong cuối tuần đó”.
Vì sao lập trình viên cứ gỡ lỗi những đoạn code đã sửa xong
Thời đó, chương trình nào có quy mô đáng kể cũng được chia ra nhiều file mã nguồn. Mỗi file được biên dịch thành một file đối tượng (object file), rồi bước liên kết ghép các file đối tượng lại thành chương trình hoàn chỉnh. Nếu sửa một file mã nguồn mà quên biên dịch lại, bước liên kết sẽ cứ thế dùng lại file đối tượng cũ. Như bài báo năm 1978 của Feldman về Make đã viết: “rất dễ quên mất những file nào cần được xử lý lại hoặc biên dịch lại sau khi có thay đổi”.
Trong cuộc phỏng vấn năm 1989 với nhà sử học Michael Mahoney, Feldman kể về một chương trình dài ba, bốn nghìn dòng mà ông viết bằng Ratfor — một biến thể của Fortran theo lối lập trình có cấu trúc — và nói rằng ông “cứ mắc mãi sai lầm kinh điển là gỡ lỗi một chương trình vốn đã đúng”. Ông đối phó theo kiểu trâu bò: trước khi lái xe về nhà, ông thường “cho biên dịch lại cả thế giới ở chế độ chạy nền”.
Cách Make quyết định phải build lại những gì
Make thay trí nhớ của lập trình viên bằng một file mô tả, ngày nay thường gọi là makefile. Trong đó, lập trình viên liệt kê từng file mà chương trình cần, ghi rõ mỗi file phụ thuộc vào những gì và được tạo ra bằng lệnh nào. Sau đó, Make kiểm tra thời điểm sửa đổi của các file. Nếu một file cũ hơn bất kỳ thứ gì mà nó phụ thuộc vào, Make chạy lại các lệnh tạo ra file đó; nếu không, Make để yên.
Bài báo năm 1978 của Feldman tóm gọn thiết kế này trong một câu: “File mô tả thực chất định nghĩa đồ thị phụ thuộc; Make tìm kiếm theo chiều sâu trên đồ thị này để xác định phần việc nào thật sự cần làm.” Nhờ các quy tắc dựng sẵn — chẳng hạn cách biến một file mã nguồn C thành file đối tượng — nhiều quan hệ phụ thuộc hoàn toàn không cần ghi ra. Bài báo cũng cảm ơn Steve Johnson “vì đã gợi ý cách tiếp cận này để kiểm soát việc bảo trì chương trình”.
Quy tắc tab mà Feldman giữ lại vì khoảng một tá người dùng
Bài báo năm 1978 nêu rõ cú pháp. Các lệnh có thể nằm “sau dấu chấm phẩy trên một dòng khai báo phụ thuộc, hoặc trên những dòng bắt đầu bằng ký tự tab nằm ngay sau một dòng khai báo phụ thuộc”.
Về sau, Feldman giải thích ký tự tab từ đâu mà ra. Khi ấy yacc của Johnson mới ra đời, còn Lex — công cụ sinh code nhận dạng các mẫu trong văn bản — thì “mới toanh”. Feldman coi Make là một cái cớ hay để học yacc và Lex. “Sau khi tự làm mình rối tung trong lần đầu thử sức với Lex, tôi chỉ làm một việc đơn giản là dùng mẫu xuống dòng rồi đến tab. Nó chạy được, thế là nó ở lại luôn.”
Tiếp đó là quyết định khiến cách làm tắt ấy bám rễ. “Rồi vài tuần sau, tôi đã có khoảng một tá người dùng, phần lớn là bạn bè, và tôi không muốn làm hỏng chuyện với nhóm người dùng sẵn có của mình. Phần còn lại, buồn thay, đã là lịch sử.”
Lời kể đó có trong cuốn sách The Art of Unix Programming (2003) của Eric S. Raymond. Năm 2015, Michael Stillwell gửi email cho Feldman về một dị bản mà anh từng nghe: một người dùng thời đầu phàn nàn về ký tự tab và được trả lời rằng đã quá muộn để thay đổi. Feldman đáp rằng câu chuyện ấy “chỉ đúng một phần”.
Ông dùng tab vì đã “gặp rắc rối với vài mẫu khác” trong phiên bản đầu tiên của Lex. Trong vòng vài tuần, đã có một tá người bạn dùng Make, “nên dù biết ‘tab ở cột 1’ là một ý tưởng tồi, tôi không muốn làm xáo trộn nhóm người dùng của mình. Thế là thay vào đó, tôi gieo họa cho hàng chục triệu người.”
Những gì giai thoại về cuối tuần ấy bỏ qua
Cụm từ “cuối tuần đó” của Feldman chỉ nói về phiên bản đầu tiên. Trong cuộc phỏng vấn năm 1989, ông kể rằng “sau một, hai tuần, tôi viết lại toàn bộ”. Sau đó, Make vẫn tiếp tục thay đổi. Nhật ký phiên bản trong mã nguồn của Make mở đầu bằng dòng “2.1 4/24/76 Base version” (bản cơ sở, ngày 24 tháng 4 năm 1976), còn bản Make đi kèm Version 7 Unix năm 1979 là phiên bản 2.58.
Trước Version 7, một phiên bản Make đề ngày tháng 4 năm 1977 đã có mặt trong Programmer’s Workbench (PWB), một bản phân phối Unix dành cho các nhóm phát triển phần mềm trong nội bộ Bell System. Doug McIlroy — người từng lãnh đạo bộ phận nghiên cứu nơi Unix ra đời ở Bell Labs — nói thẳng: “PWB không tạo ra make; Stu Feldman làm ra nó ở bộ phận nghiên cứu.”
Vì sao một cách làm tắt từ năm 1976 vẫn xuất hiện trong thông báo lỗi
Khiếm khuyết của quy tắc tab nói ra thì đơn giản. Ký tự tab và một dãy dấu cách có thể trông y hệt nhau trên màn hình, nên như Raymond viết, “cách diễn giải một makefile có thể thay đổi hẳn vì những khác biệt vô hình trong khoảng trắng”. Raymond gọi quy tắc này là “một trong những sai lầm thiết kế tệ nhất trong lịch sử Unix”.
GNU Make, phiên bản Make của dự án GNU, đến nay vẫn áp dụng quy tắc này. Tài liệu hướng dẫn của GNU Make gọi các lệnh ấy là recipe (công thức) và ghi rõ mỗi dòng recipe “phải bắt đầu bằng một ký tự tab”, rồi nói thêm: “Tám dấu cách không được tính.” Nếu thụt lề một recipe bằng tám dấu cách thay vì một tab, GNU Make thường dừng lại và báo lỗi “missing separator (did you mean TAB instead of 8 spaces?).” Phải đến năm 2010, ở phiên bản 3.82, GNU Make mới có thêm thiết lập .RECIPEPREFIX, cho phép makefile dùng một ký tự khác.
Quy tắc ấy không cản được Make lan rộng. Hiệp hội Máy tính (ACM) trao cho Feldman Giải thưởng Hệ thống Phần mềm năm 2003 vì Make, kèm nhận xét rằng “có lẽ ngày nay trên thế giới không có hệ thống phần mềm lớn nào chưa từng được một phiên bản hay một hậu duệ của Make xử lý”. Sau Bell Labs, Feldman giữ các vị trí cấp cao về nghiên cứu và kỹ thuật tại Bellcore, IBM và Google, đồng thời làm chủ tịch ACM từ năm 2006 đến năm 2008. Ông từng lấy chuyện ký tự tab làm ví dụ trong các bài giảng về kỹ thuật phần mềm.
Quy tắc tab cho thấy gì về những quyết định thiết kế ban đầu
Feldman không cân nhắc các phương án khác rồi mới chọn tab. Ông dùng mẫu đã chạy được trong lúc đang vật lộn với một phiên bản đời đầu của Lex, và trong vòng vài tuần, cái giá của việc thay đổi mẫu ấy đã không còn do một mình ông gánh. Một thiết kế không còn là bản nháp khi đã có người khác phụ thuộc vào nó, và với Make, ngưỡng ấy là khoảng một tá người bạn.
Buổi sáng uổng phí của Johnson đã mang lại cho Unix một công cụ biết tự tính ra những gì cần build lại. Còn cuộc vật lộn của Feldman với Lex thì để lại cho công cụ ấy một quy tắc mà năm mươi năm sau GNU Make vẫn mặc định áp dụng. Khi ACM vinh danh Feldman vì Make, bài phát biểu dài một phút của ông trên sân khấu mở đầu bằng câu: “Tôi muốn nói lời xin lỗi.”
Bài liên quan
Đọc bằng ngôn ngữ khác: English
Nguồn
- Eric S. Raymond, The Art of Unix Programming (2003), chương 15, “make: Automating Your Recipes”, có trích lời Stuart Feldman
- Michael S. Mahoney, dự án lịch sử truyền miệng về Unix: phỏng vấn Stu Feldman, ngày 20 tháng 9 năm 1989 (bản sao trên TUHS)
- S. I. Feldman, “Make — A Program for Maintaining Computer Programs”, Bell Laboratories, ngày 15 tháng 8 năm 1978 (tài liệu hướng dẫn Unix Seventh Edition, tập 2)
- Mã nguồn Version 7 Unix, make/ident.c: nhật ký phiên bản của Make (kho lưu trữ TUHS)
- Mã nguồn PWB/UNIX, make phiên bản 3.1(PWB), tháng 4 năm 1977 (kho lưu trữ TUHS)
- Doug McIlroy, bài viết trên mailing list TUHS về PWB và make, tháng 11 năm 2015
- Michael Stillwell, “Tabs and Makefile”, ngày 20 tháng 4 năm 2015, kèm email trả lời của Stuart Feldman
- Tài liệu hướng dẫn GNU Make, “Errors Generated by Make”
- File NEWS của GNU Make, phiên bản 3.82 (ngày 28 tháng 7 năm 2010): .RECIPEPREFIX (bản sao)
- Association for Computing Machinery, Software System Award 2003: Stuart I. Feldman
- Đại học Waterloo, Cheriton School of Computer Science, Distinguished Lecture: Stuart Feldman (tiểu sử)
- Ảnh đại diện: bảng điều khiển mặt trước của máy tính mini DEC PDP-11/70 (ảnh minh họa, không phải chiếc máy đã dùng để viết Make), Dave Fischer / Retro-Computing Society of Rhode Island, qua Wikimedia Commons, CC BY-SA 3.0
