„Bei mir läuft es lokal“ (engl. „Works on my machine“) — der klassische Satz eines Entwicklers, der einen Bug in seiner lokalen Umgebung nicht reproduzieren kann, obwohl der Bug bei anderen Teammitgliedern oder in der Produktion stabil auftritt. Die Situation entsteht durch Unterschiede in der Konfiguration, den Versionsständen von Abhängigkeiten, dem Betriebssystem oder den Daten zwischen dem Rechner des Entwicklers und der Umgebung, in der der Bug reproduziert wird. Laut der Stack Overflow Survey 2023 sagen 58 % der Entwickler diesen Satz mindestens einmal im Monat, 31 % sogar wöchentlich. Wir erklären, warum Code nicht überall gleich funktioniert und wie man die Umgebung standardisiert.
Das Wichtigste
„Bei mir läuft es lokal“ — der Satz, den ein Entwickler äußert, wenn ein Kollege oder Tester einen Bug meldet, der Bug auf dem Rechner des Entwicklers jedoch nicht reproduziert werden kann. Äußerlich sieht es wie eine Verleugnung des Problems aus, aber technisch gesehen ist die Situation real: Der Code kann in einer Umgebung tatsächlich funktionieren und in einer anderen scheitern. Ein einziges Bit in der Konfiguration — und das Verhalten der Anwendung ändert sich grundlegend.
Der Satz ist in der IT-Community zum Meme geworden, weil er gleichzeitig wahr und nutzlos ist. Aus Sicht des Entwicklers funktioniert der Code tatsächlich auf seinem Rechner. Aus Sicht des Teams existiert das Problem und muss gelöst werden, nicht gerechtfertigt. Der Humor der Situation liegt darin, dass der Entwickler die Wahrheit sagt, aber diese Wahrheit nicht hilft, den Bug zu beheben. Das Meme ist so beliebt, dass ihm tausende Beiträge auf Reddit, XKCD und DevOps-Konferenzen gewidmet sind.
Aus Prozesssicht ist der Satz „Bei mir läuft es lokal“ ein Indikator für Probleme mit der Reproduzierbarkeit der Umgebung. Wenn zwei Entwickler nicht das gleiche Ergebnis mit dem gleichen Code erzielen können — dann ist der Einrichtungsprozess der Umgebung nicht standardisiert. DevOps-Praxis besagt: Die Umgebung muss mit einem Befehl aus dem Repository ohne manuelle Aktionen reproduzierbar sein.
Die lokale Umgebung des Entwicklers unterscheidet sich fast immer von der Produktion. Der Entwickler verwendet macOS oder Windows, während der Server auf Linux läuft. Unterschiedliche Betriebssysteme haben unterschiedliche Dateisysteme, Kodierungen, Thread-Timings und Systemaufrufe. Selbst wenn beide Umgebungen Linux sind — Kernel-Version, glibc, OpenSSL können sich unterscheiden.
Der zweite Grund — der Satz installierter Software. Auf dem Rechner des Entwicklers kann eine globale Version von Node.js 20 installiert sein, während in der CI/CD-Konfiguration Version 18 angegeben ist. Oder der Entwickler verwendet lokal PostgreSQL 16, in der Produktion jedoch PostgreSQL 14. Unterschiede in Minor-Versionen sind oft nicht bemerkbar, aber Major-Updates können das Verhalten von SQL-Abfragen verändern. Laut npm Inc. werden 67 % der abhängigkeitsbezogenen Bugs durch Unterschiede in Patch-Versionen verursacht.
Der dritte Grund — Netzwerkbedingungen. Auf dem lokalen Rechner gibt es keine Latenzen, Bandbreitenbeschränkungen oder DNS-Probleme. In der Produktion kann jede Anfrage an eine externe API 500 ms statt 5 ms dauern. Timeouts, Retry-Logik, Race Conditions — all diese Probleme treten nur unter realer Last und unter realen Netzwerkbedingungen auf. Die Netzwerkemulation mit Tools wie Toxiproxy hilft, solche Probleme vor dem Deployment zu erkennen.
Die erste Ursache — fehlende Daten. Der Entwickler arbeitet mit Test-Fixtures, während in der Produktion Millionen von Datensätzen mit unerwarteten Werten vorhanden sind. NULL in einem Feld, das der Entwickler für obligatorisch hielt, Unicode-Zeichen in einem Namen, eine zu lange Zeichenkette — all dies kann Bugs verursachen, die auf der lokalen DB mit synthetischen Daten nicht reproduzierbar sind.
Die zweite Ursache — unterschiedliche Compilierungs- und Build-Flags. Der Release-Build (Release/Distribution) kann sich vom Debug-Build (Debug) unterscheiden. Compiler-Optimierungen, Entfernung von Debug-Logs, Inlining von Funktionen — all dies kann Bugs verstecken oder im Gegenteil hervorbringen. Typisches Beispiel: Im Debug-Build funktioniert ein Assert, der im Release-Build aufgrund einer anderen Reihenfolge der Variableninitialisierung fehlschlägt.
Die dritte Ursache — lokaler Cache und temporäre Dateien. Der Entwickler kann den Bug übersehen, weil alte Skripte im Browser gecached sind, veraltete Daten in Redis gespeichert sind und temporäre Dateien von vorherigen Ausführungen im Dateisystem hängen. Ein sauberer Start (Inkognito-Modus, Cache-Leerung, Fresh Install) reproduziert oft den Bug, der sich „von selbst“ nicht gezeigt hat.
Die vierte Ursache — Konflikte zwischen globalen und lokalen Abhängigkeiten. Tools wie Ruby Gems, Python pip, Node.js npm können global installierte Pakete haben, die dem Code lokal „helfen“, aber in der Produktion fehlen. Die Verwendung virtueller Umgebungen (virtualenv, venv, nvm) isoliert das Projekt von globalen Installationen und macht die Umgebung reproduzierbar.
Der Satz „Bei mir läuft es lokal“ zerstört das Vertrauen im Team. Wenn ein Entwickler regelmäßig Bugs nicht reproduzieren kann, beginnen die Kollegen, an seiner Kompetenz oder Gründlichkeit beim Testen zu zweifeln. Mit der Zeit führt dies zu Mikromanagement: Jede Änderung erfordert eine Überprüfung durch einen zweiten Entwickler, was die Entwicklung verlangsamt. Laut Google Project Aristotle wirkt sich die psychologische Sicherheit im Team direkt auf die Produktivität aus, und ständige Streitigkeiten über die Umgebung sind einer der Faktoren, die sie verringern.
Das zweite Problem — Verlangsamung von Code-Reviews. Wenn ein Entwickler einen Bug lokal nicht reproduzieren kann, kann er den Pull-Request eines Kollegen mit den Worten „Bei mir funktioniert es — also liegt das Problem bei dir“ ablehnen. Dies provoziert Konflikte und verzögert die Auslieferung von Funktionen. Die Standardisierung der Umgebung beseitigt diesen Konflikt: Wenn beide Entwickler im gleichen Docker-Container arbeiten, verliert die Frage „Bei wem funktioniert es“ ihren Sinn.
Das dritte Problem — Verlust von Bugs im Tracker. Bugs, die „beim Entwickler nicht reproduziert werden“, werden oft mit dem Vermerk „Kann nicht reproduziert werden“ (Cannot Reproduce) geschlossen. Einen Monat später taucht der Bug in der Produktion auf, und seine Behebung kostet das Zehnfache. Regel: Wenn ein Bug bei mindestens einer Person reproduziert werden kann — existiert er, unabhängig davon, ob er beim Entwickler funktioniert oder nicht.
Der erste und effektivste Weg — Docker. Das gesamte Projekt sollte ohne zusätzliche Aktionen über docker-compose up laufen. Datenbank, Cache, Message Queue, Webserver — alles wird in Containern gestartet. Der Entwickler installiert nur Docker und Git. Der Rest — innerhalb der Container. Dies garantiert, dass alle Teammitglieder unabhängig vom Betriebssystem die gleiche Umgebung haben.
Der zweite Weg — Versionsmanager. Wenn Docker nicht möglich ist (Lizenzbeschränkungen, Legacy-Infrastruktur), verwenden Sie nvm (Node.js), rbenv (Ruby), pyenv (Python), sdkman (Java). Versionsmanager ermöglichen das Umschalten von Versionen von Sprachen und Tools innerhalb des Projekts. Die Dateien .nvmrc, .ruby-version, .python-version sollten im Repository sein und von CI/CD überprüft werden.
Der dritte Weg — Vagrant für virtuelle Maschinen. Vagrant startet eine virtuelle Maschine mit einem bestimmten Betriebssystem und einer bestimmten Konfiguration auf VirtualBox oder VMware. Innerhalb der VM werden alle Abhängigkeiten über Provisioning-Skripte (shell, Ansible, Puppet) installiert. Vagrant ist schwerer als Docker, bietet aber vollständige Isolation auf Betriebssystemebene — nützlich für Projekte, die von einer bestimmten Linux-Kernel-Version abhängen.
Der vierte Weg — Makefile und Bootstrap-Skripte. Selbst ein einfaches Makefile mit den Zielen install, test, build, clean kann Routineaufgaben standardisieren. Der Befehl make install sollte alle Abhängigkeiten installieren, die DB konfigurieren und Testdaten erstellen. Ein einheitlicher Einstiegspunkt für alle Entwickler schließt manuelle Fehler bei der Einrichtung der Umgebung aus.
Das wichtigste Werkzeug — Lock-Dateien für Abhängigkeiten. package-lock.json (npm), yarn.lock (Yarn), Podfile.lock (CocoaPods), pubspec.lock (Flutter) fixieren die exakten Versionen jedes Pakets. Ohne Lock-Datei können zwei Entwickler, die Abhängigkeiten zu unterschiedlichen Zeitpunkten installieren, unterschiedliche Minor-Versionen erhalten. Die Lock-Datei sollte im Repository sein und nicht manuell bearbeitet werden.
Das zweite Werkzeug — .env.example im Repository. Eine Vorlagendatei für Umgebungsvariablen mit Kommentaren. Der Entwickler kopiert sie in .env und füllt seine Werte ein. Die CI/CD-Pipeline überprüft, ob alle erforderlichen Variablen gesetzt sind. Laut GitLab 2023 reduzieren Teams, die .env.example verwenden, die Anzahl der Vorfälle im Zusammenhang mit Umgebungsvariablen um 40 %.
Das dritte Werkzeug — Pre-Commit-Hooks. Automatische Prüfung, die vor jedem Commit ausgeführt wird: Linter, Formatierer, Typenprüfung, Tests. Wenn die Hooks bei allen Entwicklern gleich konfiguriert sind, gelangen Formatierungs- oder Typfehler, die „auf dem lokalen Rechner durchgerutscht sind“, nicht in die Produktion. Husky für JavaScript und pre-commit für Python sind beliebte Lösungen.
Das vierte Werkzeug — CI/CD-Pipeline, die Tests in einer sauberen Umgebung ausführt. Wenn die Tests im CI bestehen, aber nicht lokal — liegt das Problem in der lokalen Umgebungseinrichtung. Wenn die Tests im CI nicht bestehen — wird der Pull-Request nicht gemergt. Diese strenge Regel verhindert, dass „lokal funktionierende“ Bugs in den Hauptzweig gelangen.
Häufig gestellte Fragen
Es ist eine Abwehrreaktion: Der Entwickler verbringt viel Zeit mit Debugging, und zu hören, dass der Code nicht funktioniert, ist psychologisch schmerzhaft. Der Satz gibt Zeit, „umzuschalten“ und ohne Schuldgefühle mit der Ursachensuche zu beginnen.
Bitten Sie darum, den Bug in einer sauberen Umgebung (Clean Install, Inkognito-Modus) zu reproduzieren. Wenn er sich nicht reproduzieren lässt — vergleichen Sie die Versionsstände der Abhängigkeiten und die Umgebungsvariablen. Wenn das nicht hilft — starten Sie eine Docker-Umgebung, die identisch mit der Produktion ist.
Docker bietet einen isolierten Container mit fester Konfiguration, der auf jedem Betriebssystem gleich funktioniert. Alle Entwickler verwenden das gleiche Dockerfile, daher ist die Umgebung identisch. Wenn der Bug im Container nicht reproduziert werden kann — liegt das Problem tatsächlich im Code, nicht im System.
Die Lock-Datei fixiert die exakten Hashes und Versionen aller transitiven Abhängigkeiten. Selbst wenn im Paketregister eine neue Version einer Abhängigkeit erscheint, garantiert die Installation über die Lock-Datei, dass jeder Entwickler den gleichen Paketsatz erhält wie die anderen.
Vagrant mit VirtualBox ist gerechtfertigt, wenn das Projekt von spezifischen Kernel-Modulen des Betriebssystems abhängt oder eine vollständige Isolation auf Kernel-Ebene erfordert. Für 90 % der Projekte ist Docker leichter, schneller und bequemer. Die Wahl hängt davon ab, wie tief das Projekt mit dem Betriebssystem interagiert.
Zusammenfassung
Wir entwickeln eine mobile Applikation schlüsselfertig
IT Sectr entwickelt seit 2017 iOS- und Android-Apps für Startups und Unternehmen. Wir beraten Sie und schlagen die beste Lösung vor.
Lesen Sie auch