„Werkt op mijn machine” (Engels: „Works on my machine”) — de klassieke uitspraak van een ontwikkelaar die een bug niet kan reproduceren in zijn lokale omgeving, hoewel de bug stabiel optreedt bij andere teamleden of in productie. De situatie ontstaat door verschillen in configuratie, versies van afhankelijkheden, besturingssysteem of gegevens tussen de machine van de ontwikkelaar en de omgeving waar de bug wordt gereproduceerd. Volgens de Stack Overflow Survey 2023 zeggen 58% van de ontwikkelaars deze zin minstens een keer per maand, en 31% wekelijks. We onderzoeken waarom code niet overal hetzelfde werkt en hoe de omgeving te standaardiseren.
Belangrijkste
„Werkt op mijn machine” — de zin die een ontwikkelaar uitspreekt wanneer een collega of tester een bug meldt, maar de bug niet reproduceert op de machine van de ontwikkelaar. Uiterlijk lijkt het op ontkenning van het probleem, maar technisch is de situatie reële: code kan echt werken in de ene omgeving en falen in een andere. Het verschil van één bit in configuratie — en het gedrag van de applicatie verandert drastisch.
De zin is een meme geworden in de IT-gemeenschap omdat hij tegelijkertijd waar en nutteloos is. Vanuit het perspectief van de ontwikkelaar — de code werkt echt op zijn machine. Vanuit het perspectief van het team — het probleem bestaat en moet worden opgelost, niet gerechtvaardigd. De humor van de situatie is dat de ontwikkelaar de waarheid spreekt, maar die waarheid helpt niet om de bug te verhelpen. De meme is zo populair dat er duizenden berichten aan zijn gewijd op Reddit, XKCD en DevOps-conferenties.
Vanuit procesperspectief is de zin „werkt op mijn machine” een indicator van problemen met reproduceerbaarheid van de omgeving. Als twee ontwikkelaars niet hetzelfde resultaat kunnen krijgen op dezelfde code — dan is het proces van omgevingsconfiguratie niet gestandaardiseerd. DevOps-praktijk stelt: de omgeving moet reproduceerbaar zijn met één commando uit de repository, zonder handmatige handelingen.
De lokale omgeving van de ontwikkelaar verschilt bijna altijd van productie. De ontwikkelaar gebruikt macOS of Windows, terwijl de server op Linux draait. Verschillende besturingssystemen hebben verschillende bestandssystemen, coderingen, thread-timings en systeemaanroepen. Zelfs als beide omgevingen Linux zijn — de kernelversie, glibc, OpenSSL kunnen verschillen.
De tweede reden — de set geïnstalleerde software. Op de machine van de ontwikkelaar kan een globale versie Node.js 20 zijn geïnstalleerd, terwijl in de CI/CD-configuratie versie 18 staat. Of de ontwikkelaar gebruikt PostgreSQL 16 lokaal, maar in productie PostgreSQL 14. Verschillen in minor-versies zijn vaak niet merkbaar, maar grote updates kunnen het gedrag van SQL-query's veranderen. Volgens npm Inc. wordt 67% van de bugs gerelateerd aan afhankelijkheden veroorzaakt door verschillen in patch-versies.
De derde reden — netwerkomstandigheden. Op de lokale machine zijn er geen vertragingen, bandbreedtebeperkingen of DNS-problemen. In productie kan elk verzoek aan een externe API 500 ms duren in plaats van 5 ms. Time-outs, retry-logica, race conditions — al deze problemen manifesteren zich alleen onder echte belasting en in echte netwerkomstandigheden. Netwerkemulatie via tools zoals Toxiproxy helpt deze problemen voor de deploy te identificeren.
De eerste oorzaak — ontbrekende gegevens. De ontwikkelaar werkt met testfixtures, terwijl er in productie miljoenen records zijn met onverwachte waarden. NULL in een veld dat de ontwikkelaar als verplicht beschouwde, een Unicode-teken in een naam, een te lange string — dit alles kan bugs veroorzaken die niet reproduceerbaar zijn op een lokale database met synthetische gegevens.
De tweede oorzaak — verschillende compilatie- en build-vlaggen. De release-build (Release/Distribution) kan verschillen van de debug-build (Debug). Compileroptimalisaties, verwijdering van debug-logs, inlining van functies — dit alles kan bugs verbergen of juist manifesteren. Typisch voorbeeld: in de debug-build werkt een assert die faalt in de release-build vanwege een andere initialisatievolgorde van variabelen.
De derde oorzaak — lokale cache en tijdelijke bestanden. De ontwikkelaar kan de bug niet opmerken omdat in de browser oude scripts zijn gecached, in Redis verouderde gegevens zijn opgeslagen en in het bestandssysteem tijdelijke bestanden van eerdere uitvoeringen hangen. Een schone start (incognitomodus, cache leegmaken, fresh install) reproduceert vaak de bug die zich niet „vanzelf” manifesteerde.
De vierde oorzaak — conflicten tussen globale en lokale afhankelijkheden. Tools zoals Ruby gems, Python pip, Node.js npm kunnen wereldwijd geïnstalleerde pakketten hebben die de code lokaal „helpen” werken, maar ontbreken in productie. Het gebruik van virtuele omgevingen (virtualenv, venv, nvm) isoleert het project van globale installaties en maakt de omgeving reproduceerbaar.
De zin „werkt op mijn machine” vernietigt het vertrouwen in het team. Als een ontwikkelaar regelmatig bugs niet kan reproduceren, gaan collega’s twijfelen aan zijn competentie of grondigheid van testen. Na verloop van tijd leidt dit tot micromanagement: elke wijziging moet door een tweede ontwikkelaar worden gecontroleerd, wat de ontwikkeling vertraagt. Volgens Google Project Aristotle beïnvloedt psychologische veiligheid in het team direct de productiviteit, en constante discussies over de omgeving zijn een van de factoren die deze verminderen.
Het tweede probleem — vertraging van code review. Als een ontwikkelaar de bug lokaal niet kan reproduceren, kan hij de pull request van een collega afwijzen met de woorden „bij mij werkt het — dus het probleem zit bij jou”. Dit veroorzaakt conflicten en vertraagt de levering van functionaliteiten. Standaardisatie van de omgeving verwijdert dit conflict: als beide ontwikkelaars in dezelfde Docker-container werken, verliest de vraag „bij wie werkt het” zijn betekenis.
Het derde probleem — verlies van bugs in de tracker. Bugs die „niet reproduceerbaar zijn bij de ontwikkelaar” worden vaak gesloten met de opmerking „niet reproduceerbaar” (Cannot Reproduce). Een maand later duikt de bug op in productie en kost het oplossen ervan 10 keer meer. Regel: als een bug bij ten minste één persoon reproduceert — bestaat hij, ongeacht of het bij de ontwikkelaar werkt of niet.
De eerste en meest effectieve manier — Docker. Het hele project moet starten met docker-compose up zonder extra handelingen. Database, cache, berichtenwachtrij, webserver — alles wordt in containers opgezet. De ontwikkelaar installeert alleen Docker en Git. De rest — binnen de containers. Dit garandeert dat alle teamleden dezelfde omgeving hebben, ongeacht het besturingssysteem.
De tweede manier — versiebeheerders. Als Docker niet mogelijk is (licentiebeperkingen, legacy-infrastructuur), gebruik dan nvm (Node.js), rbenv (Ruby), pyenv (Python), sdkman (Java). Versiebeheerders maken het mogelijk om te schakelen tussen versies van talen en tools binnen het project. Bestanden .nvmrc, .ruby-version, .python-version moeten in de repository staan en worden gecontroleerd door CI/CD.
De derde manier — Vagrant voor virtuele machines. Vagrant zet een virtuele machine op met een gespecificeerd besturingssysteem en configuratie bovenop VirtualBox of VMware. Binnen de VM worden alle afhankelijkheden geïnstalleerd via provisioning-scripts (shell, Ansible, Puppet). Vagrant is zwaarder dan Docker, maar biedt volledige isolatie op OS-niveau — nuttig voor projecten die afhankelijk zijn van een specifieke Linux-kernelversie.
De vierde — makefile en bootstrap-scripts. Zelfs een eenvoudige Makefile met doelen install, test, build, clean kan routinematige handelingen standaardiseren. Het commando make install moet alle afhankelijkheden installeren, de database configureren en testgegevens aanmaken. Één enkel toegangspunt voor alle ontwikkelaars sluit handmatige fouten bij het instellen van de omgeving uit.
De belangrijkste tool — lock-bestanden voor afhankelijkheden. package-lock.json (npm), yarn.lock (Yarn), Podfile.lock (CocoaPods), pubspec.lock (Flutter) leggen de exacte versies van elk pakket vast. Zonder lock-bestand kunnen twee ontwikkelaars die afhankelijkheden op verschillende tijdstippen installeren, verschillende minor-versies krijgen. Het lock-bestand moet in de repository staan en niet handmatig worden bewerkt.
De tweede tool — .env.example in de repository. Een sjabloonbestand voor omgevingsvariabelen met commentaar. De ontwikkelaar kopieert het naar .env en vult zijn eigen waarden in. De CI/CD-pijplijn controleert of alle verplichte variabelen zijn ingesteld. Volgens GitLab 2023 verminderen teams die .env.example gebruiken het aantal incidenten met omgevingsvariabelen met 40%.
De derde tool — pre-commit hooks. Automatische controle die voor elke commit wordt uitgevoerd: linter, formatter, typecontrole, tests. Als de hooks bij alle ontwikkelaars hetzelfde zijn geconfigureerd, zullen opmaak- of typefouten die „op de lokale machine door de beugel konden” niet in productie komen. Husky voor JavaScript en pre-commit voor Python zijn populaire oplossingen.
De vierde — CI/CD-pijplijn die tests in een schone omgeving uitvoert. Als tests in CI slagen maar niet lokaal — ligt het probleem bij de configuratie van de lokale omgeving. Als tests niet slagen in CI — wordt de pull request niet gemerged. Deze strikte regel voorkomt dat bugs die „lokaal werken” in de hoofdvertakking terechtkomen.
Veelgestelde vragen
Het is een afweerreactie: de ontwikkelaar besteedt veel tijd aan debuggen en horen dat de code niet werkt is psychologisch pijnlijk. De zin geeft tijd om „om te schakelen” en de oorzaak te gaan zoeken zonder schuldgevoel.
Vraag hem de bug te reproduceren in een schone omgeving (clean install, incognitomodus). Als het niet reproduceert — vergelijk de versies van afhankelijkheden en omgevingsvariabelen. Als dat niet helpt — zet een Docker-omgeving op die identiek is aan de productie.
Docker biedt een geïsoleerde container met een vaste configuratie die op elk besturingssysteem hetzelfde werkt. Alle ontwikkelaars gebruiken dezelfde Dockerfile, dus de omgeving is identiek. Als de bug niet reproduceert in de container — dan zit het probleem echt in de code, niet in het systeem.
Het lock-bestand legt de exacte hashes en versies vast van alle transitieve afhankelijkheden. Zelfs als er een nieuwe versie van een afhankelijkheid in de pakketregister verschijnt, garandeert installatie via het lock-bestand dat elke ontwikkelaar dezelfde set pakketten krijgt als de anderen.
Vagrant met VirtualBox is gerechtvaardigd als het project afhankelijk is van specifieke OS-kernelmodules of volledige isolatie op kernel-niveau vereist. Voor 90% van de projecten is Docker lichter, sneller en handiger. De keuze hangt af van hoe diep het project met het besturingssysteem interageert.
Samenvatting
We ontwikkelen een mobiele applicatie turnkey
IT Sectr creëert sinds 2017 iOS- en Android-applicaties voor startups en bedrijven. We adviseren u en stellen de beste oplossing voor.
Lees ook