Chạy trên máy của tôi: định nghĩa, tại sao xảy ra và cách ngăn chặn

Tác giả: IT Sectr Đã đăng: 2026-07-30 Thời gian đọc: 8 phút

“Chạy trên máy của tôi” (tiếng Anh: “Works on my machine”) — câu nói kinh điển của lập trình viên không thể tái hiện lỗi trong môi trường cục bộ của mình, mặc dù lỗi xuất hiện ổn định ở các thành viên khác trong nhóm hoặc trên môi trường production. Tình huống này phát sinh do sự khác biệt về cấu hình, phiên bản phụ thuộc, hệ điều hành hoặc dữ liệu giữa máy của lập trình viên và môi trường nơi lỗi được tái hiện. Theo Stack Overflow Survey 2023, 58% lập trình viên nói câu này ít nhất một lần một tháng, và 31% — hàng tuần. Tìm hiểu tại sao mã không chạy giống nhau ở mọi nơi và cách chuẩn hóa môi trường.

Điểm chính

  • “Works on my machine” — một meme và vấn đề thực tế, cho thấy sự khác biệt môi trường trong nhóm
  • Nguyên nhân chính: phiên bản phụ thuộc khác nhau, biến môi trường, hệ điều hành và cài đặt khu vực
  • Vấn đề được giải quyết bằng cách chuẩn hóa môi trường qua Docker hoặc Vagrant
  • Tệp lock (package-lock, Podfile.lock) cố định phiên bản phụ thuộc cho tất cả lập trình viên
  • Đồng bộ thường xuyên với kho và cài đặt phụ thuộc sạch làm giảm tần suất vấn đề

“Chạy trên máy của tôi” có nghĩa là gì

“Chạy trên máy của tôi” — câu nói mà lập trình viên thường nói khi đồng nghiệp hoặc người kiểm thử báo cáo lỗi, nhưng lỗi không tái hiện được trên máy của lập trình viên. Nhìn bên ngoài có vẻ như phủ nhận vấn đề, nhưng về mặt kỹ thuật, tình huống này có thật: mã có thể hoạt động trong môi trường này và thất bại trong môi trường khác. Chỉ một bit cấu hình khác đi là hành vi của ứng dụng thay đổi hoàn toàn.

Câu nói này đã trở thành một meme trong cộng đồng IT vì nó vừa đúng vừa vô dụng. Theo quan điểm của lập trình viên — mã thực sự chạy trên máy của anh ấy. Theo quan điểm của nhóm — vấn đề tồn tại và cần được giải quyết, không phải biện minh. Sự hài hước của tình huống là lập trình viên nói sự thật, nhưng sự thật này không giúp sửa lỗi. Meme này phổ biến đến nỗi hàng ngàn bài đăng trên Reddit, XKCD và các hội nghị DevOps được dành cho nó.

Theo quan điểm quy trình, câu nói “chạy trên máy của tôi” là dấu hiệu của vấn đề về khả năng tái tạo môi trường. Nếu hai lập trình viên không thể có cùng kết quả trên cùng một mã — thì quy trình thiết lập môi trường không được chuẩn hóa. Thực hành DevOps khẳng định: môi trường phải có thể tái tạo bằng một lệnh từ kho, không cần thao tác thủ công.

Tại sao môi trường cục bộ khác với production

Môi trường cục bộ của lập trình viên hầu như luôn khác với production. Lập trình viên sử dụng macOS hoặc Windows, trong khi máy chủ chạy Linux. Các hệ điều hành khác nhau có hệ thống tệp, bảng mã, thời gian xử lý luồng và lời gọi hệ thống khác nhau. Ngay cả khi cả hai môi trường đều là Linux — phiên bản kernel, glibc, OpenSSL có thể khác nhau.

Nguyên nhân thứ hai — bộ phần mềm được cài đặt. Trên máy của lập trình viên có thể có phiên bản toàn cục Node.js 20, trong khi cấu hình CI/CD chỉ định phiên bản 18. Hoặc lập trình viên sử dụng PostgreSQL 16 cục bộ, còn trên production — PostgreSQL 14. Sự khác biệt về phiên bản phụ thường không đáng kể, nhưng các bản cập nhật chính có thể thay đổi hành vi của truy vấn SQL. Theo npm Inc., 67% lỗi liên quan đến phụ thuộc là do sự khác biệt về phiên bản patch.

Nguyên nhân thứ ba — điều kiện mạng. Trên máy cục bộ không có độ trễ, giới hạn băng thông hoặc sự cố DNS. Trên production, bất kỳ yêu cầu nào đến API bên ngoài có thể mất 500 ms thay vì 5 ms. Timeout, logic retry, race conditions — tất cả những vấn đề này chỉ xuất hiện dưới tải thực tế và trong điều kiện mạng thực tế. Mô phỏng mạng qua các công cụ như Toxiproxy giúp phát hiện các vấn đề này trước khi triển khai.

Nguyên nhân điển hình không tái hiện được lỗi cục bộ

Nguyên nhân đầu tiên — thiếu dữ liệu. Lập trình viên làm việc với dữ liệu kiểm thử, trong khi trên production có hàng triệu bản ghi với các giá trị không mong đợi. NULL trong trường mà lập trình viên cho là bắt buộc, ký tự Unicode trong tên, chuỗi quá dài — tất cả điều này có thể gây ra lỗi không thể tái hiện trên DB cục bộ với dữ liệu tổng hợp.

Nguyên nhân thứ hai — cờ biên dịch và xây dựng khác nhau. Bản dựng release (Release/Distribution) có thể khác với bản dựng debug (Debug). Tối ưu hóa trình biên dịch, loại bỏ log debug, nội tuyến hàm — tất cả điều này có thể che giấu hoặc ngược lại, bộc lộ lỗi. Ví dụ điển hình: trong bản dựng debug, assert hoạt động nhưng trong bản release lại thất bại do thứ tự khởi tạo biến khác nhau.

Nguyên nhân thứ ba — bộ nhớ đệm cục bộ và tệp tạm thời. Lập trình viên có thể không nhận thấy lỗi vì trình duyệt đã lưu cache các tập lệnh cũ, Redis lưu dữ liệu lỗi thời, và hệ thống tệp còn các tệp tạm thời từ các lần chạy trước. Một lần chạy sạch (chế độ ẩn danh, xóa cache, fresh install) thường tái hiện được lỗi mà “tự nó” không xuất hiện.

Nguyên nhân thứ tư — xung đột phụ thuộc toàn cục và cục bộ. Các công cụ như Ruby gems, Python pip, Node.js npm có thể có các gói được cài đặt toàn cục giúp mã chạy cục bộ nhưng lại thiếu trên production. Sử dụng môi trường ảo (virtualenv, venv, nvm) cô lập dự án khỏi các cài đặt toàn cục và làm cho môi trường có thể tái tạo được.

Ảnh hưởng đến làm việc nhóm và lòng tin

Câu nói “chạy trên máy của tôi” phá hủy lòng tin trong nhóm. Nếu lập trình viên thường xuyên không thể tái hiện lỗi, đồng nghiệp bắt đầu nghi ngờ năng lực hoặc độ kỹ lưỡng kiểm tra của anh ta. Theo thời gian, điều này dẫn đến quản lý vi mô: mỗi thay đổi đều yêu cầu kiểm tra bởi một lập trình viên khác, làm chậm quá trình phát triển. Theo Google Project Aristotle, an toàn tâm lý trong nhóm ảnh hưởng trực tiếp đến năng suất, và các tranh cãi liên tục về môi trường là một trong những yếu tố làm giảm nó.

Vấn đề thứ hai — làm chậm code review. Nếu lập trình viên không thể tái hiện lỗi cục bộ, anh ta có thể từ chối pull request của đồng nghiệp với lý do “máy tôi chạy được — vậy vấn đề là của bạn”. Điều này gây ra xung đột và làm chậm việc cung cấp tính năng. Chuẩn hóa môi trường loại bỏ xung đột này: nếu cả hai lập trình viên làm việc trong cùng một container Docker, câu hỏi “ai chạy được” mất ý nghĩa.

Vấn đề thứ ba — mất lỗi trong trình theo dõi. Các lỗi “không tái hiện được ở lập trình viên” thường bị đóng với ghi chú “Không thể tái hiện” (Cannot Reproduce). Một tháng sau, lỗi xuất hiện trên production và việc sửa chữa tốn kém gấp 10 lần. Quy tắc: nếu lỗi tái hiện được ở ít nhất một người — nó tồn tại, bất kể nó có chạy trên máy của lập trình viên hay không.

Cách chuẩn hóa môi trường lập trình viên

Cách đầu tiên và hiệu quả nhất — Docker. Toàn bộ dự án phải chạy được qua docker-compose up mà không cần thao tác bổ sung. Cơ sở dữ liệu, bộ nhớ đệm, hàng đợi thông báo, máy chủ web — tất cả đều chạy trong container. Lập trình viên chỉ cài đặt Docker và Git. Phần còn lại — bên trong container. Điều này đảm bảo tất cả thành viên trong nhóm có môi trường giống nhau bất kể hệ điều hành.

Cách thứ hai — trình quản lý phiên bản. Nếu Docker không khả thi (hạn chế giấy phép, cơ sở hạ tầng legacy), hãy sử dụng nvm (Node.js), rbenv (Ruby), pyenv (Python), sdkman (Java). Trình quản lý phiên bản cho phép chuyển đổi phiên bản ngôn ngữ và công cụ trong dự án. Các tệp .nvmrc, .ruby-version, .python-version phải có trong kho và được CI/CD kiểm tra.

Cách thứ ba — Vagrant cho máy ảo. Vagrant khởi động máy ảo với hệ điều hành và cấu hình được chỉ định trên VirtualBox hoặc VMware. Bên trong VM, tất cả phụ thuộc được cài đặt qua các tập lệnh provisioning (shell, Ansible, Puppet). Vagrant nặng hơn Docker nhưng cung cấp sự cô lập hoàn toàn ở cấp hệ điều hành — hữu ích cho các dự án phụ thuộc vào phiên bản kernel Linux cụ thể.

Cách thứ tư — makefile và tập lệnh bootstrap. Ngay cả một Makefile đơn giản với các mục tiêu install, test, build, clean cũng có thể chuẩn hóa các tác vụ thường ngày. Lệnh make install phải cài đặt tất cả phụ thuộc, cấu hình DB và tạo dữ liệu kiểm thử. Điểm vào duy nhất cho tất cả lập trình viên loại bỏ lỗi thủ công trong quá trình thiết lập môi trường.

Công cụ ngăn chặn sự khác biệt môi trường

Công cụ chính — tệp lock phụ thuộc. package-lock.json (npm), yarn.lock (Yarn), Podfile.lock (CocoaPods), pubspec.lock (Flutter) cố định phiên bản chính xác của mỗi gói. Không có tệp lock, hai lập trình viên cài đặt phụ thuộc vào thời điểm khác nhau có thể nhận được các phiên bản phụ khác nhau. Tệp lock phải có trong kho và không được chỉnh sửa thủ công.

Công cụ thứ hai — .env.example trong kho. Tệp mẫu biến môi trường có chú thích. Lập trình viên sao chép nó vào .env và điền giá trị của mình. Đường ống CI/CD kiểm tra xem tất cả các biến bắt buộc đã được thiết lập chưa. Theo GitLab 2023, các nhóm sử dụng .env.example giảm số sự cố liên quan đến biến môi trường xuống 40%.

Công cụ thứ ba — hook pre-commit. Kiểm tra tự động chạy trước mỗi lần commit: lint, định dạng, kiểm tra kiểu, kiểm thử. Nếu các hook được cấu hình giống nhau ở tất cả lập trình viên, thì các lỗi định dạng hoặc kiểu mà đã “vượt qua trên máy cục bộ” sẽ không đến được production. Husky cho JavaScript và pre-commit cho Python là các giải pháp phổ biến.

Công cụ thứ tư — đường ống CI/CD chạy kiểm thử trong môi trường sạch. Nếu kiểm thử đạt trong CI nhưng không đạt cục bộ — vấn đề nằm ở cấu hình môi trường cục bộ. Nếu kiểm thử không đạt trong CI — pull request không được hợp nhất. Quy tắc nghiêm ngặt này ngăn các lỗi “chạy ở cục bộ” xâm nhập vào nhánh chính.

Câu hỏi thường gặp

Tại sao lập trình viên thường nói “máy tôi chạy được” thay vì tìm nguyên nhân ngay lập tức?

Đó là phản ứng phòng vệ: lập trình viên dành nhiều thời gian gỡ lỗi, và nghe rằng mã không chạy được là điều tổn thương tâm lý. Câu nói này cho thời gian để “chuyển đổi” và bắt đầu tìm nguyên nhân mà không có cảm giác tội lỗi.

Làm thế nào để phản ứng nếu lập trình viên nói “chạy trên máy của tôi”?

Yêu cầu tái hiện lỗi trong môi trường sạch (clean install, chế độ ẩn danh). Nếu không tái hiện được — so sánh phiên bản phụ thuộc và biến môi trường. Nếu không được — khởi động môi trường Docker giống hệt production.

Docker giải quyết vấn đề “Works on my machine” như thế nào?

Docker cung cấp một container cô lập với cấu hình cố định hoạt động giống nhau trên mọi hệ điều hành. Tất cả lập trình viên sử dụng cùng một Dockerfile, do đó môi trường giống hệt nhau. Nếu lỗi không tái hiện trong container — thì vấn đề thực sự nằm ở mã, không phải hệ thống.

Tệp lock giúp tránh sự khác biệt như thế nào?

Tệp lock cố định hash và phiên bản chính xác của tất cả phụ thuộc chuyển tiếp. Ngay cả khi phiên bản mới của phụ thuộc được phát hành trong registry gói, việc cài đặt từ tệp lock đảm bảo rằng mỗi lập trình viên nhận được cùng một tập gói như những người khác.

Có nên sử dụng máy ảo thay vì Docker không?

Vagrant với VirtualBox phù hợp nếu dự án phụ thuộc vào các mô-đun kernel cụ thể của hệ điều hành hoặc yêu cầu sự cô lập hoàn toàn ở cấp kernel. Đối với 90% dự án, Docker nhẹ hơn, nhanh hơn và tiện lợi hơn. Lựa chọn phụ thuộc vào mức độ tương tác của dự án với hệ điều hành.

Tổng kết

  • “Chạy trên máy của tôi” — không phải là lời bào chữa, mà là triệu chứng của sự khác biệt môi trường trong nhóm
  • Nguyên nhân chính: phiên bản phụ thuộc và công cụ khác nhau, biến môi trường, hệ điều hành và dữ liệu
  • Câu nói phá hủy lòng tin trong nhóm và làm chậm code review và cung cấp tính năng
  • Docker — công cụ chuẩn hóa môi trường chính cho tất cả lập trình viên
  • Tệp lock và .env.example cố định cấu hình trong kho
  • Hook pre-commit và đường ống CI/CD tự động kiểm tra mã trong môi trường sạch
  • Môi trường chuẩn hóa tiết kiệm hàng giờ gỡ lỗi và loại bỏ các lỗi “kỳ diệu”

Chúng tôi sẽ phát triển ứng dụng di động chìa khóa trao tay

IT Sectr tạo các ứng dụng iOS và Android cho các công ty khởi nghiệp và doanh nghiệp từ năm 2017. Chúng tôi sẽ tư vấn và đề xuất giải pháp tốt nhất cho bạn.

Thảo luận dự án

Đọc thêm