“Gumagana sa aking makina” (Ingles: “Works on my machine”) — ang klasikong parirala ng isang developer na hindi maaaring kopyahin ang bug sa kanyang lokal na kapaligiran, kahit na ang bug ay palaging lumalabas sa ibang miyembro ng team o sa produksyon. Ang sitwasyon ay lumitaw dahil sa mga pagkakaiba sa configuration, bersyon ng dependencies, operating system, o data sa pagitan ng makina ng developer at ng kapaligiran kung saan ang bug ay nape-reproduce. Ayon sa Stack Overflow Survey 2023, 58% ng mga developer ay nagsasabi ng pariralang ito kahit isang beses sa isang buwan, at 31% — lingguhan. Alamin natin kung bakit hindi pareho ang takbo ng code sa lahat ng dako at kung paano i-standardize ang kapaligiran.
Mga Pangunahing Punto
“Gumagana sa aking makina” — ang pariralang sinasabi ng isang developer kapag ang isang kasamahan o tester ay nag-ulat ng bug, ngunit sa makina ng developer ang bug na ito ay hindi maaaring kopyahin. Sa panlabas, ito ay mukhang pagtanggi sa problema, ngunit teknikal na ang sitwasyon ay totoo: ang code ay maaaring gumana sa isang kapaligiran at bumagsak sa isa pa. Ang pagkakaiba ng isang bit ng configuration — at ang pag-uugali ng aplikasyon ay nagbabago nang lubusan.
Ang parirala ay naging isang meme sa IT community dahil ito ay parehong totoo at walang silbi. Mula sa pananaw ng developer — ang code ay talagang gumagana sa kanyang makina. Mula sa pananaw ng team — ang problema ay umiiral at kailangang lutasin, hindi bigyang-katwiran. Ang katatawanan ng sitwasyon ay ang developer ay nagsasabi ng totoo, ngunit ang katotohanang iyon ay hindi nakakatulong na ayusin ang bug. Ang meme ay napakapopular na libu-libong post sa Reddit, XKCD, at DevOps conferences ay nakatuon dito.
Mula sa pananaw ng proseso, ang pariralang “gumagana sa aking makina” ay isang indikasyon ng mga problema sa reproducibility ng kapaligiran. Kung ang dalawang developer ay hindi makakuha ng parehong resulta sa parehong code — nangangahulugan ito na ang proseso ng pag-set up ng kapaligiran ay hindi na-standardize. Ang DevOps practice ay nagsasaad: ang kapaligiran ay dapat na reproducible sa pamamagitan ng isang utos mula sa repository nang walang manu-manong aksyon.
Ang lokal na kapaligiran ng developer ay halos palaging naiiba sa produksyon. Ang developer ay gumagamit ng macOS o Windows, habang ang server ay tumatakbo sa Linux. Iba’t ibang operating system ay may iba’t ibang file system, encoding, timing ng thread, at system call. Kahit na ang parehong kapaligiran ay Linux — ang bersyon ng kernel, glibc, OpenSSL ay maaaring magkaiba.
Ang pangalawang dahilan — ang set ng naka-install na software. Sa makina ng developer ay maaaring naka-install ang global na bersyon ng Node.js 20, habang sa CI/CD configuration ay nakasaad ang bersyon 18. O ang developer ay gumagamit ng PostgreSQL 16 lokal, at sa produksyon — PostgreSQL 14. Ang mga pagkakaiba sa minor na bersyon ay madalas hindi napapansin, ngunit ang mga major update ay maaaring magbago ng pag-uugali ng SQL queries. Ayon sa npm Inc., 67% ng mga bug na may kaugnayan sa dependencies ay sanhi ng pagkakaiba sa patch versions.
Ang pangatlong dahilan — mga kondisyon ng network. Sa lokal na makina ay walang pagkaantala, limitasyon sa bandwidth, at problema sa DNS. Sa produksyon, ang anumang request sa external na API ay maaaring tumagal ng 500 ms sa halip na 5 ms. Timeout, retry logic, race conditions — lahat ng problemang ito ay lumalabas lamang sa ilalim ng tunay na load at sa tunay na kondisyon ng network. Ang emulation ng network sa pamamagitan ng mga tool tulad ng Toxiproxy ay tumutulong na matukoy ang mga problemang ito bago ang deploy.
Ang unang dahilan — kakulangan ng data. Ang developer ay nagtatrabaho sa mga test fixtures, habang sa produksyon ay may milyun-milyong record na may hindi inaasahang halaga. NULL sa field na itinuturing ng developer na sapilitan, Unicode character sa pangalan, masyadong mahabang string — lahat ng ito ay maaaring magdulot ng mga bug na hindi maaaring kopyahin sa lokal na database na may sintetikong data.
Ang pangalawang dahilan — iba’t ibang flag ng compilation at build. Ang release build (Release/Distribution) ay maaaring naiiba sa debug build (Debug). Mga optimization ng compiler, pagtanggal ng debug logs, inlining ng functions — lahat ng ito ay maaaring itago o, sa kabaligtaran, magpakita ng mga bug. Tipikal na halimbawa: sa debug build ay gumagana ang isang assert na bumagsak sa release build dahil sa ibang pagkakasunod-sunod ng initialization ng mga variable.
Ang pangatlong dahilan — lokal na cache at pansamantalang file. Maaaring hindi mapansin ng developer ang bug dahil ang mga lumang script ay naka-cache sa browser, ang mga lumang data ay naka-save sa Redis, at ang mga pansamantalang file mula sa mga nakaraang pagtakbo ay nakabitin sa file system. Ang malinis na pagtakbo (incognito mode, pag-clear ng cache, fresh install) ay madalas na nagkopya ng bug na hindi lumabas “nang mag-isa”.
Ang ikaapat na dahilan — mga conflict ng global at lokal na dependencies. Ang mga tool tulad ng Ruby gems, Python pip, Node.js npm ay maaaring may globally naka-install na mga pakete na “tumutulong” sa code na gumana nang lokal, ngunit wala sa produksyon. Ang paggamit ng virtual na kapaligiran (virtualenv, venv, nvm) ay naghihiwalay sa proyekto mula sa global na pag-install at ginagawang reproducible ang kapaligiran.
Ang pariralang “gumagana sa aking makina” ay sumisira sa tiwala sa team. Kung ang isang developer ay hindi regular na makopya ang mga bug, ang mga kasamahan ay magsisimulang magduda sa kanyang kakayahan o pagiging masusi sa pag-test. Sa paglipas ng panahon, ito ay humahantong sa micromanagement: bawat pagbabago ay nangangailangan ng pag-verify ng pangalawang developer, na nagpapabagal sa pag-develop. Ayon sa Google Project Aristotle, ang psychological safety sa team ay direktang nakakaapekto sa produktibidad, at ang patuloy na pagtatalo tungkol sa kapaligiran ay isa sa mga salik ng pagbaba nito.
Ang pangalawang problema — pagbagal ng code review. Kung ang isang developer ay hindi makopya ang bug nang lokal, maaari niyang tanggihan ang pull request ng kasamahan sa mga salitang “sa akin gumagana — ibig sabihin ang problema ay nasa iyo”. Ito ay nagdudulot ng mga conflict at naantala ang paghahatid ng mga feature. Ang pag-standardize ng kapaligiran ay nag-aalis ng conflict na ito: kung ang parehong developer ay nagtatrabaho sa parehong Docker container, ang tanong na “sa kanino gumagana” ay nawawalan ng kahulugan.
Ang pangatlong problema — pagkawala ng mga bug sa tracker. Ang mga bug na “hindi nape-reproduce sa developer” ay madalas na isinasara na may tala na “hindi maaaring kopyahin” (Cannot Reproduce). Pagkalipas ng isang buwan, ang bug ay lumalabas sa produksyon, at ang pag-aayos nito ay nagkakahalaga ng 10 beses na mas mahal. Panuntunan: kung ang isang bug ay nape-reproduce kahit sa isang tao — ito ay umiiral, hindi alintana kung ito ay gumagana sa developer o hindi.
Ang una at pinakamabisang paraan — Docker. Ang buong proyekto ay dapat tumakbo sa pamamagitan ng docker-compose up nang walang karagdagang aksyon. Database, cache, message queue, web server — lahat ay pinaandar sa mga container. Ang developer ay nag-i-install lamang ng Docker at Git. Ang natitira — sa loob ng mga container. Ito ay ginagarantiyahan na ang lahat ng miyembro ng team ay may parehong kapaligiran anuman ang OS.
Ang pangalawang paraan — mga version manager. Kung hindi posible ang Docker (mga limitasyon sa lisensya, legacy infrastructure), gamitin ang nvm (Node.js), rbenv (Ruby), pyenv (Python), sdkman (Java). Ang mga version manager ay nagpapahintulot ng paglipat sa pagitan ng mga bersyon ng wika at tool sa loob ng proyekto. Ang mga file na .nvmrc, .ruby-version, .python-version ay dapat na nasa repository at suriin ng CI/CD.
Ang pangatlong paraan — Vagrant para sa virtual machine. Ang Vagrant ay nagpapatakbo ng virtual machine na may tinukoy na OS at configuration sa ibabaw ng VirtualBox o VMware. Sa loob ng VM, ang lahat ng dependencies ay naka-install sa pamamagitan ng provisioning scripts (shell, Ansible, Puppet). Ang Vagrant ay mas mabigat kaysa sa Docker, ngunit nagbibigay ng kumpletong paghihiwalay sa antas ng OS — kapaki-pakinabang para sa mga proyektong umaasa sa isang partikular na bersyon ng Linux kernel.
Ang ikaapat — makefile at bootstrap scripts. Kahit isang simpleng Makefile na may mga target na install, test, build, clean ay maaaring mag-standardize ng mga routine action. Ang utos na make install ay dapat mag-install ng lahat ng dependencies, i-configure ang database, at lumikha ng test data. Isang punto ng pagpasok para sa lahat ng developer ay nag-aalis ng mga manual error sa pag-set up ng kapaligiran.
Ang pangunahing tool — mga lock-file ng dependencies. package-lock.json (npm), yarn.lock (Yarn), Podfile.lock (CocoaPods), pubspec.lock (Flutter) ay nagtatakda ng eksaktong bersyon ng bawat pakete. Nang walang lock-file, dalawang developer na nag-install ng dependencies sa magkaibang oras ay maaaring makakuha ng magkaibang minor na bersyon. Ang lock-file ay dapat nasa repository at hindi manu-manong i-edit.
Ang pangalawang tool — .env.example sa repository. Isang template file ng mga variable ng kapaligiran na may mga komento. Kinokopya ito ng developer sa .env at pinupunan ang kanyang sariling mga halaga. Ang CI/CD pipeline ay sumusuri na ang lahat ng sapilitang variable ay nakatakda. Ayon sa GitLab 2023, ang mga team na gumagamit ng .env.example ay nagbabawas ng bilang ng mga insidente na may kaugnayan sa mga variable ng kapaligiran ng 40%.
Ang pangatlong tool — pre-commit hooks. Awtomatikong pagsusuri na tumatakbo bago ang bawat commit: linter, formatter, pagsusuri ng uri, mga test. Kung ang mga hook ay naka-configure nang pareho sa lahat ng developer, ang mga error sa pag-format o uri na “pumasa sa lokal na makina” ay hindi makakarating sa produksyon. Husky para sa JavaScript at pre-commit para sa Python ay mga popular na solusyon.
Ang ikaapat — CI/CD pipeline na nagpapatakbo ng mga test sa malinis na kapaligiran. Kung ang mga test ay pumasa sa CI ngunit hindi lokal — ang problema ay nasa pag-set up ng lokal na kapaligiran. Kung ang mga test ay hindi pumasa sa CI — ang pull request ay hindi na-merge. Ang mahigpit na patakarang ito ay nag-aalis ng pagpasok ng mga bug na “gumagana nang lokal” sa pangunahing branch.
Mga Madalas Itanong
Ito ay isang depensang reaksyon: ang developer ay gumugugol ng maraming oras sa debugging, at marinig na ang code ay hindi gumagana ay psychologically masakit. Ang parirala ay nagbibigay ng oras upang “ lumipat” at simulan ang paghahanap ng dahilan nang walang pakiramdam ng pagkakasala.
Hilingin sa kanya na kopyahin ang bug sa isang malinis na kapaligiran (clean install, incognito mode). Kung hindi ito ma-kopya — ihambing ang mga bersyon ng dependencies at mga variable ng kapaligiran. Kung hindi ito makakatulong — magpatakbo ng Docker environment na kapareho ng produksyon.
Ang Docker ay nagbibigay ng isoladong container na may nakapirming configuration na gumagana nang pareho sa anumang OS. Lahat ng developer ay gumagamit ng parehong Dockerfile, kaya ang kapaligiran ay magkapareho. Kung ang bug ay hindi ma-kopya sa container — ibig sabihin ang problema ay talagang nasa code, hindi sa system.
Ang lock-file ay nagtatakda ng eksaktong mga hash at bersyon ng lahat ng transitive dependencies. Kahit na may bagong bersyon ng dependency na lumabas sa package registry, ang pag-install batay sa lock-file ay ginagarantiyahan na ang bawat developer ay makakakuha ng parehong set ng mga pakete tulad ng iba.
Ang Vagrant na may VirtualBox ay makatwiran kung ang proyekto ay umaasa sa mga partikular na module ng OS kernel o nangangailangan ng kumpletong paghihiwalay sa antas ng kernel. Para sa 90% ng mga proyekto, ang Docker ay mas magaan, mas mabilis, at mas maginhawa. Ang pagpili ay depende sa kung gaano kalalim ang interaksyon ng proyekto sa OS.
Mga Konklusyon
Gagawa kami ng mobile application na turnkey
Gumagawa ang IT Sectr ng mga iOS at Android application para sa mga startup at negosyo mula noong 2017. Magpapayo kami sa iyo at magmumungkahi ng pinakamahusay na solusyon.
Basahin din