Cảnh báo ranh giới của hiệu ứng Makefile
(blog.yossarian.net)- Hiệu ứng Makefile chỉ mẫu hành vi không dùng một công cụ phức tạp hoặc xa lạ ngay từ đầu, mà sao chép một cấu hình từng hoạt động tốt trước đây rồi chỉnh sửa cho phù hợp với tình huống mới
- Trong giai đoạn giải quyết vấn đề, việc tái sử dụng ví dụ đã được kiểm chứng có thể là một phản ứng kỹ thuật hợp lý giúp giảm rủi ro phát sinh lỗi
- Tuy nhiên, từ góc độ thiết kế, đây cũng có thể được xem là tín hiệu về tính khả dụng: công cụ khó hoặc phiền phức khi dùng ngay từ đầu, nên các thay đổi cứ tiếp tục tích lũy trên bản sao
- Không chỉ với Make mà cả trong GitHub Actions, GitLab CI/CD, cấu hình linter·formatter và hệ thống build cũng xuất hiện luồng sao chép rồi chỉnh sửa tương tự
- Người thiết kế công cụ cần kiểm tra xem cấu hình và cú pháp riêng có thực sự cần thiết không, có thể tái sử dụng các thành ngữ CLI quen thuộc không, và liệu người dùng có đang bị đẩy vào chỗ phải phụ thuộc vào copy-paste hay không
Hiệu ứng Makefile là gì
- Hiệu ứng Makefile là hiện tượng không viết mới một công cụ có mức độ phức tạp hoặc xa lạ nhất định, mà sao chép rồi chỉnh sửa một ví dụ “đã hoạt động tốt” từ trước
- Luồng điển hình như sau
- Cần hoàn thành một tác vụ phổ biến, và một tác vụ rất giống hoặc y hệt như vậy đã từng được thực hiện trước đó
- Make hoặc công cụ tương tự được chọn là công cụ phù hợp cho tác vụ đó
- Thay vì viết
Makefilemới, kỹ sư sao chépMakefilecủa tác vụ trước rồi sửa cho đến khi nó chạy được trong môi trường mới
- Make chỉ là ví dụ tiêu biểu; vì công cụ mà từng nhóm hoặc từng cá nhân thành thạo là khác nhau, nên có những nhóm công cụ dễ bị hiệu ứng này hơn và có những nhóm ít bị hơn tương đối
- Trò đùa “heritage
Makefile” chỉ tình huống mộtMakefileđược truyền lại từ kỹ sư kỳ cựu, giáo sư hoặc người đi trước, rồi tiếp tục được kế thừa với chỉ vài chỉnh sửa nhỏ
Vấn đề thiết kế công cụ mà việc sao chép lặp lại bộc lộ
- Ở thời điểm cần giải quyết vấn đề ngay, tái sử dụng ví dụ đang chạy được có thể là một lựa chọn gọn gàng
- Vì phần lớn tác vụ không thay đổi, về lý thuyết điều này có thể tạo ra ít bug mới hơn
- Hiệu ứng Makefile về bản chất không phải lúc nào cũng là hiện tượng xấu hay kém hiệu quả
- Nhưng ở thời điểm thiết kế, nó là tín hiệu cho thấy công cụ hoặc cách áp dụng công cụ quá phức tạp hoặc quá phiền toái
- Người dùng lặp đi lặp lại việc sao chép lời giải có sẵn thay vì giải bài toán từ đầu
- Nếu hiệu ứng này xuất hiện cả với những trường hợp áp dụng đơn giản, thì công cụ có thể đang quá phức tạp so với mục đích đó
- Mẫu hình này cũng xuất hiện ở các công cụ thiên về cấu hình ngoài Make
- Trong cấu hình CI/CD, người ta thường sao chép YAML từ cấu hình gần nhất đã chạy được của GitHub Actions hoặc GitLab CI/CD rồi lặp lại quá trình chỉnh sửa
- Cấu hình linter và formatter thường sao chép một bộ quy tắc mặc định giữa các dự án, rồi siết chặt hoặc nới lỏng theo điều kiện cục bộ
- Những cấu hình không hề đơn giản của hệ thống build rất dễ trở nên giống với hệ thống build trước đó
Chi phí phát sinh cho học tập, debug và bảo mật
- Những công cụ có hiệu ứng Makefile mạnh có thể cản trở việc học sâu trên diện rộng
- Một số ít người thành thạo sẽ cấu hình công cụ, còn những người khác chỉ hiểu phần cần thiết rồi sao chép và chỉnh sửa
- Đồ thị phụ thuộc của hệ thống build là độ phức tạp mang tính bản chất, nhưng việc phải nhớ sự khác nhau giữa
$<và$^trong Make thì không phải vậy
- Nếu thiếu hỗ trợ chẩn đoán và debug, người dùng sẽ phải dựa vào cách chạy công cụ nhiều lần để nhặt từng manh mối nhỏ
- Trong CI/CD, việc debug kiểu print thường phải đi qua mạng và các lớp điều phối VM trung gian
- Xét về bảo mật, rủi ro cũng có thể tăng lên
- Các biện pháp bảo mật thường đòi hỏi phải hiểu sâu “vì sao” của hành vi hệ thống
- Những hệ thống nơi hiệu ứng Makefile xuất hiện thường cho phép nhầm lẫn giữa code và data, hoặc kiểu nhầm lẫn in-band signaling nói chung
- Template injection trong GitHub Actions là ví dụ tiêu biểu
Những câu hỏi người thiết kế công cụ nên tự kiểm tra
- Khi tạo công cụ mới, cần xem liệu người dùng có thể viết từ đầu hay không, hay họ buộc phải đi tìm một “ví dụ đang chạy được” để sao chép
- Các câu hỏi nên rà soát gồm
- Khả năng cấu hình có thực sự cần thiết không
- Cú pháp riêng có thực sự bắt buộc không
- Có thể tái sử dụng cú pháp và thành ngữ quen thuộc từ công cụ khác hoặc CLI hay không
- Ngay cả người tạo ra công cụ cũng phải copy-paste cách dùng hay không, và nếu có thì người dùng khác cũng rất có thể sẽ như vậy
- Về thuật ngữ “cargo cult”, bài viết của Ken Shirriff bàn về vấn đề đây là một thuật ngữ chuyên môn không phù hợp
1 bình luận
Các ý kiến trên Hacker News
“Một hệ thống phức tạp hoạt động được luôn luôn được phát hiện là đã tiến hóa từ một hệ thống đơn giản từng hoạt động. Một hệ thống phức tạp được thiết kế từ đầu sẽ không bao giờ hoạt động, và cũng không thể vá sửa để khiến nó hoạt động. Bạn phải bắt đầu lại với một hệ thống đơn giản hoạt động được”
— John Gall (1975), Systemantics: How Systems Really Work and How They Fail
https://en.wikipedia.org/wiki/John_Gall_(author)#Gall's_law
Thường thì các tính năng như những vết xước bắt đầu được gắn thêm để xử lý các trường hợp đặc biệt, và điều đó trông như tín hiệu rằng những ngoại lệ như vậy sẽ tiếp tục tăng vô tận
Có một khác biệt tinh tế nhưng quan trọng giữa “áp dụng kinh nghiệm” và “thiết kế một hệ thống hoàn toàn mới xoay quanh một điều cá nhân thấy khó chịu”
Tôi có một giả thuyết khác. Khoảng 10% lập trình viên thực sự hiểu nguyên lý nên có thể bắt đầu thứ gì đó từ con số không, và khoảng 40% nữa có thể xử lý công việc hằng ngày bằng cách copy-paste từ code local, Stack Overflow, GitHub, LLM, đồng thời đại khái biết chuyện gì đang diễn ra
50% còn lại thì không biết nhiều ngoài vài câu đố LeetCode, và cũng thiếu hiểu biết thực sự về những thứ họ copy-paste
Với phân bố như vậy, rất có khả năng hơn một nửa Makefile chỉ là các mảnh copy-paste chạy tạm được. Nếu lấy từ nơi vốn đã chạy, thì trước mắt coi như xong việc và chuyển sang ticket tiếp theo
Tôi không đổ lỗi cho bản thân công cụ. Makefile đã rất quen thuộc và cũng không dài dòng trong các dự án nhỏ. Nó có thể là lựa chọn tệ cho một con quái vật 10.000 file, nhưng tôi từng thấy Makefile gọn gàng ngay cả trong các dự án khổng lồ. Cá nhân tôi có lẽ sẽ không chọn nó đầu tiên, nhưng tôi thích Makefile và đã dùng lẻ tẻ hơn 30 năm
Ví dụ, khi một lập trình viên C++ xử lý ticket thì cần biết đến bao nhiêu tầng của stack? Có cần biết cả tên thanh ghi CPU không? Một nhà nghiên cứu AI chỉ dùng Jupyter thì cần biết đến đâu? Việc học tối đa về công cụ và stack là đáng khuyến khích, nhưng thời gian thì có hạn
Nếu thấy trong Makefile có các rule riêng lẻ để tạo một file đơn lẻ, tôi hầu như luôn coi đó là sai lầm
Thông thường chỉ nên có rule build tổng quát dùng để build tất cả file thuộc một kiểu nhất định
Makefile hầu như không nên chứa danh sách file nguồn hay danh sách phụ thuộc, mà chỉ cần danh sách các thư mục chứa file nguồn
Make nên tìm file bằng cách quét các thư mục nguồn, phân loại theo từng kiểu, tạo danh sách phụ thuộc và gọi rule build phù hợp. Ít nhất với GNU make thì việc này rất đơn giản và cũng có trong hướng dẫn sử dụng
Nếu viết theo cách này, dù dự án có 1 file hay 10.000 file thì công sức tạo hoặc sửa Makefile đều nhỏ đến mức có thể bỏ qua. Cũng không cần cập nhật Makefile mỗi khi file nguồn được tạo, đổi tên, di chuyển hay xóa
Vì mỗi ngôn ngữ và framework có “trình tự” build khác nhau nên tôi không thể nhớ hết. Nhưng
$ makethì dễTôi đồng ý rằng nhiều lập trình viên ứng dụng năng suất cao có thể không thiết lập được một dự án mới từ đầu, nhưng điều đó thường là vấn đề biết đúng các quy tắc ma thuật và câu thần chú để nhiều công cụ khớp với nhau, hơn là vấn đề hiểu biết sâu sắc
Một yếu tố khác tôi thấy ở nơi làm việc là các công cụ và hệ thống mà lập trình viên rồi cũng sẽ phải đụng đến lúc nào đó, nhưng thường cảm thấy không đáng để học
Ví dụ như hệ thống build và cấu hình CI. Chúng là cần thiết, nhưng lập trình viên không nghĩ mình phải xử lý chúng hằng ngày. CI bị xem như một hệ thống “thiết lập rồi quên”, và phản ứng thường là liệu có cần học hết những thứ này chỉ để build ứng dụng không
Lập trình viên kỳ vọng nó “cứ thế chạy”, và nếu có độ phức tạp thì cho rằng một đội khác, tức những người có vai trò như tôi, sẽ xử lý. Vì vậy mỗi lần lập trình viên phải chạm vào hệ thống này, họ có động lực rất mạnh để sao chép cấu hình lần cuối còn chạy được, chuyển nó sang, rồi quay lại với công việc “thực sự”
Cách giải quyết tốt nhất là gặp lập trình viên ở khoảng giữa. Cần cung cấp công cụ đơn giản hoặc phức tạp phù hợp với công việc, có tài liệu đầy đủ, và giảm tối đa niềm tin rằng đó là “ma thuật”. Những công cụ như Make thường thất bại ở điểm này vì bị cảm nhận là quá phức tạp và giống hộp đen
Vì thế vòng phản hồi trở nên cực kỳ dài, và bản thân công việc trở thành một nỗi khổ lớn
Nếu lập trình viên có thể chạy cục bộ một phần hoặc toàn bộ tác vụ “CI/CD”, họ có thể quan sát, kiểm soát và hiểu nó. Điều này giúp rất nhiều trong việc tạo cảm giác sở hữu và yên tâm, thay vì cảm giác “CI/CD là chuyện ở thế giới khác”
Không phải lúc nào cũng hiệu quả. Tôi từng thấy một nhóm hai người có CI/CD dạng lớp bọc mỏng mà vẫn giả vờ đó là một quy trình ngoài hành tinh và từ chối động vào
Tương tự, CI cần có quyền sở hữu, và cách để trách nhiệm rơi vào lập trình viên cuối cùng định dùng CI không phải là một cách làm việc hiệu quả
Giải pháp là đừng mang công cụ mà các tập đoàn lớn dùng về chỉ vì các tập đoàn lớn dùng nó. Có thể là ý kiến không được ưa chuộng, nhưng tôi cho rằng CI/CD không cần thiết ở phần lớn nơi nó đang được dùng
Ngay cả khi số bước tăng lên đôi chút, nên tìm cách build và triển khai với số bộ phận chuyển động ít nhất tuyệt đối. Sau đó hãy cân nhắc cẩn thận chi phí của việc tự động hóa phần nào. Đưa một hệ thống lớn vào chỉ để làm việc đơn giản thường không đáng về dài hạn
Nếu thật sự cần CI/CD, bạn sẽ biết vì có điểm đau. Nếu hệ thống đó gây đau cho lập trình viên thì nó không phải công cụ phù hợp
make“quá phức tạp và giống hộp đen”, thì tức là bạn chưa thấycmakeMake và Makefile cực kỳ đơn giản nếu không phải do autoconf tự động tạo ra. Nếu là thứ autoconf tạo thì không nên sửa, vì đó là sản phẩm build. Nếu có thể thì tốt nhất cũng nên bỏ luôn autoconf
Nhìn rộng hơn, hiệu ứng này thực sự tồn tại. Bạn có thể là nạn nhân của nó, cũng có thể tận dụng nó. Cách tận dụng là như sau: viết một ít mã hoặc copy-paste từ đâu đó, dùng thử trong dự án, rồi tinh chỉnh theo nhu cầu. Khi bắt đầu dự án tiếp theo, lại chép đoạn mã đó vào và chỉnh cho phù hợp với dự án thứ hai. Xem liệu có thể áp ngược các thay đổi về dự án ban đầu không. Khi cả hai đều chạy và được đồng bộ, hãy tách mã đó ra thành thư viện. Đôi khi cần thêm nhiều dự án nữa để tinh luyện nó thành hình hài giống một thư viện. Trường hợp tốt nhất là công bố mã nguồn mở để người khác cũng dùng được
Vì Git không ghi timestamp nội bộ, mtime của file có thể được cập nhật dù nội dung giống hệt, và thực tế chuyện này xảy ra thường xuyên, gây ra các lần rebuild không cần thiết
Nó cũng hoàn toàn không xử lý tốt các công cụ hiện đại có input hoặc output là cả thư mục, hoặc các công cụ mà trước khi chạy chưa thể biết tên output
Tôi thích make, đã dùng tốt bất chấp các nhược điểm, và biết cả các cách lách lẫn cách lách của các cách lách đó. Nhưng một khi công cụ bắt đầu không khớp hoàn hảo với kỳ vọng của make, độ khó và độ phức tạp để tạo Makefile đúng sẽ tăng vọt rất nhanh
Không đùa đâu. Việc tồn tại khái niệm
make cleantự nó đã là bằng chứng quyết địnhMakefile có lẽ là một phép ví dụ sai. Vấn đề mà phần lớn mọi người gặp với Makefile là họ viết nó quá hiếm. Họ có cảm giác chung về việc make làm gì, nhưng không có trí nhớ cơ bắp để viết từ đầu
Dù vậy ý chính vẫn đúng. Tôi đã thấy rất nhiều mã copy từ web còn sót đầy mã chết không thực sự được dùng. Thói quen tốt là cứ xóa tiếp cho đến khi hỏng, rồi chỉ hoàn nguyên đúng phần bị hỏng đó. Nên xóa nhiều nhất có thể, và chắc chắn xóa hết những gì hiện không dùng
Giải pháp từ trước đến nay là tạo một repository riêng chứa ghi chú và các chương trình ví dụ bằng shell script cho những công cụ, thư viện, framework này
Mỗi khi phải dùng lại công cụ, tôi tham khảo ghi chú để gợi lại trí nhớ, và nếu học được điều mới thì cập nhật ghi chú. Giờ cũng có thể trỏ LLM vào repository đó rồi đặt câu hỏi
Repository đó là dùng cá nhân và chứa thông tin về các công cụ có sẵn công khai. Kiến thức đặc thù của tổ chức thì tương tự nhưng được lưu trong repository riêng, và bị hủy khi hợp đồng với khách hàng hoặc nhà tuyển dụng kết thúc
Cách gọi đúng nhất cho chuyện này là phát triển kiểu sùng bái hàng viện trợ. “Cargo cult” xuất hiện ở Thái Bình Dương trong Thế chiến II: những cư dân bản địa trên đảo nhìn thấy các máy bay kỳ diệu chở thức ăn, rượu và vật tư đến; họ bắt chước hành vi của binh lính, làm máy bay bằng tre và thiết bị bằng dừa, rồi cầu xin các vị thần cảm động để hàng hóa lại đến
Vấn đề là người dân trên đảo không hiểu khoa học đằng sau những thứ như máy bay, máy vô tuyến, súng
Tương tự, lập trình viên kiểu sùng bái hàng viện trợ nhìn thấy kết quả có thể đạt được nhưng không hiểu các nguyên lý thứ nhất, nên bắt chước hành vi của các “tư tế cấp cao” trong công nghệ, hy vọng có thể sao chép thành công
Vì vậy họ cứ lặp đi lặp lại việc sao chép, dán, thử, mày mò, tìm kiếm, kéo, đẩy, điều chỉnh, với hy vọng lần này nó sẽ tạm chạy được, ít nhất là trên một tập dữ liệu cụ thể vào tối thứ Ba
Thực tế là họ lặp lại những hành động hay nghi thức tự thân không có hiệu quả, trong khi mong nhận được kết quả mà một công ty khác từng đạt được khi thực hiện cùng nghi thức
Điều bài gốc nói thì khác. Thứ được sao chép là mã, và bản thân nó có tác dụng. Ta có thể kiểm thử và phán đoán nó có chạy hay không
Sự phân biệt này quan trọng. Vì triệu chứng của hiệu ứng Makefile trong bài gốc và cargo cult là khác nhau, nếu coi chúng là một thì sẽ khó chẩn đoán. Trong cargo cult, người ta lãng phí thời gian vào những việc thực sự không hoạt động vì mê tín. Trong hiệu ứng Makefile, thứ đó được chứng minh là hoạt động, nhưng các mảnh giống như cơ quan thoái hóa bị sao chép theo, khiến việc bảo trì ngày càng khó hơn
Cụ thể, tôi không nghĩ nó hoàn toàn giống cargo cult. Cargo cult hàm ý thiếu hiểu biết. Nhưng bạn vẫn có thể hiểu rất rõ một hệ thống, song vì độ phức tạp nội tại của hệ thống đó thúc đẩy việc sao chép-dán, nên phần lớn thời gian bạn vẫn sống theo cách ấy. Đó là ý chính của bài
Thường thì tôi hiểu bên trong đang xảy ra gì, nhưng bản thân việc chuyển sang “chế độ” đó đã nặng về mặt nhận thức, nên nếu có thể thì tôi tránh
Những công cụ thuộc loại này thường là công cụ vận hành như CI/CD, k8s, Docker: độ phức tạp rất lớn nhưng không phải vấn đề cốt lõi tôi đang giải. Make đặc biệt khó tránh chuyển ngữ cảnh, nên dạo này tôi thường né luôn
Nó không liên quan đến thần chú kỳ diệu; tôi biết mình đang đánh đổi gì. Dù vậy vẫn có rủi ro biến thành hiệu ứng Makefile
“Hiệu ứng Makefile giống với các hiện tượng khác như cargo cult, bình thường hóa sai lệch, ‘ngôn ngữ chỉ-ghi’. Trong bài này, tôi sẽ lập luận rằng nó hơi khác từng hiện tượng đó ở chỗ nó không nhất thiết kém hiệu quả hay xấu về bản chất, mà là hệ quả của một thiết kế cụ thể”
Nếu vậy, nếu ai đó thật sự muốn “thoát khỏi hòn đảo và lái máy bay”, rốt cuộc đại học có phải con đường gần với “thật” nhất không?
Tôi biết khó nói tuyệt đối, có nhiều ngoại lệ, và mỗi người có đủ kiểu biện minh “tôi không làm được việc đó”. Ở đây tôi hỏi từ góc nhìn lý thuyết tối ưu. Con đường tối ưu để tránh hành vi sùng bái là hiểu toàn bộ, và “toàn bộ” đó chẳng phải đến từ giáo dục bậc cao sao?
Về mặt logic, ngay cả khi chăm chỉ học bằng sách để bắt kịp hoặc vượt qua “tính hoàn chỉnh” của đại học, thì ở một khía cạnh nào đó nó vẫn có vẻ thiếu so với thời gian ở lớp học cùng các nhà giáo có tri thức. Ngay cả giả sử cùng một người nỗ lực như nhau ở cả hai phía
Một yếu tố khác là tần suất sử dụng. Tôi viết một tài liệu lớn bằng LaTeX không quá một lần mỗi năm
LaTeX ở mức tôi dùng không phải công cụ khó, nhưng nếu mỗi năm chỉ dùng vài tuần thì không thể nào nhớ được các chi tiết cách dùng, nên tôi thường bắt đầu tài liệu mới bằng cách sao chép-dán từ tài liệu cũ
Nhưng với LaTeX, dù trước đây tôi đã dùng rất nhiều, tôi luôn sao chép-dán từ template. Bài trình bày beamer và hình TikZ còn tệ hơn: tôi sao chép từ bài trình bày hoặc hình trước đó thay vì từ template
TikZ thì tôi nghĩ bản thân công cụ vốn phức tạp và tôi chưa học đủ
LaTeX thì tôi nghĩ mình đã học đủ, nên có thể có lý do khác
Theo tôi, đó có thể là vấn đề giá trị mặc định hợp lý. Một công cụ tốt nên cung cấp các mặc định tốt. Nhưng LaTeX không phải công cụ tốt theo tiêu chí này. Tài liệu của tôi thường bắt đầu kiểu như sau Phần lớn là để có hỗ trợ mặc định hợp lý cho ký tự không phải ASCII cần cho tiếng mẹ đẻ của tôi, hoặc bật các mặc định bình thường như giấy A4, microtype. Với các công cụ hiện đại như pandoc/Markdown thì có thể không cần
Vì vậy mục đích của việc sao chép-dán những thứ như vậy thường là để có được các mặc định tốt mà một công cụ tốt hơn lẽ ra đã cung cấp ngay từ đầu
Nếu không viết toán hoặc không chỉ viết văn bản thuần tiếng Anh, thì LaTeX gần như là “không kèm pin”
Đây là phát triển dẫn dắt bởi copy-paste [0], và cũng không phải câu chuyện chỉ liên quan đến Makefile. Nó liên quan đến việc cả ngành sao chép code khắp nơi mà không hề biết mình đang sao chép cái gì
Thành thật mà nói, tôi nghĩ Copilot đã làm chuyện này tệ hơn. Vì mọi người đang nhắm mắt chấp nhận những khối code vào codebase
[0] https://andrew.grahamyooll.com/blog/copy-pasta-driven-develo...
make, nhưng chưa bao giờ được dành thời gian xử lý nó hằng ngày cho đến khi thuộc nằm lòng hoàn toànĐến một lúc nào đó tôi chỉ bỏ cuộc. Nó trở thành thứ tri thức bí truyền khó hiểu, không thể tạo được trí nhớ cơ bắp, và mỗi khi cần lại phải học lại từ đầu. Vì vậy tôi chuyển sang công cụ đơn giản hơn
Sự mất mát của deep work không phải lỗi của những lập trình viên giỏi, mà là lỗi của những người phía kinh doanh
Tôi đã thấy quá nhiều trường hợp sao chép nguyên Makefile từ dự án khác rồi chỉnh cho “chạy được”, trong khi vẫn để nguyên các bước build và target thừa, hoàn toàn không liên quan
Cá nhân tôi thấy điều này rất khó chịu
Bằng thử-sai à? Vậy thì chúc vui nhé :p
Đúng là có nhiều công cụ phức tạp hơn mức cần thiết, nhưng tôi không nghĩ bản thân Make là ví dụ phù hợp cho điều đó
Với các công cụ hiện đại, vấn đề độ phức tạp thường bị làm trầm trọng hơn bởi tài liệu thiếu sót, tài liệu kiểu cookbook, tài liệu không giải thích mô hình khái niệm cần thiết để suy luận công cụ hoạt động ra sao, và tài liệu không có đặc tả đủ chi tiết và chính xác
Make thì không như vậy. Tài liệu của nó tốt, và nếu thật sự dành thời gian đọc tài liệu thì không khó hiểu
Cách tiếp cận kiểu cookbook ở trên dẫn đến một văn hóa xem nhẹ tầm quan trọng của việc học và hiểu công cụ mình dùng, cũng như tầm quan trọng của tài liệu kỹ lưỡng để làm được điều đó. Hoặc cũng có thể quan hệ nhân quả là ngược lại
Dù sao thì tôi nghĩ vấn đề nằm ở chỗ chúng ta dành quá ít thời gian để tạo tài liệu công cụ toàn diện và cập nhật, cũng như chưa đủ chú ý thiết kế công cụ sao cho việc viết tài liệu như vậy là khả thi ngay từ đầu, và phân bổ quá ít nguồn lực cho việc dạy và học các công cụ cần thiết