Was kann die Ursache für neue Fehler sein, die an anderer Stelle auftreten, wenn ein bekannter Fehler behoben wird?


14

Während einer Diskussion erzählte mir einer meiner Kollegen, dass er einige Probleme mit seinem aktuellen Projekt hat, während er versucht, Fehler zu beheben. "Wenn ich einen Fehler behebe, funktioniert etwas anderes nicht mehr an anderer Stelle", sagte er.

Ich begann darüber nachzudenken, wie das passieren könnte, kann es aber nicht herausfinden.

  • Ich habe manchmal ähnliche Probleme, wenn ich zu müde / schläfrig bin, um die Arbeit korrekt auszuführen und einen Überblick über den Teil des Codes zu erhalten, an dem ich gearbeitet habe. Hier scheint das Problem einige Tage oder Wochen zu bestehen und hat nichts mit dem Fokus meines Kollegen zu tun.
  • Ich kann mir auch vorstellen, dass dieses Problem bei einem sehr großen, schlecht gemanagten Projekt auftritt , bei dem Teamkollegen keine Ahnung haben, wer was tut und welche Auswirkungen auf die Arbeit anderer Menschen sich auf sie auswirken können. Dies ist auch hier nicht der Fall: Es ist ein eher kleines Projekt mit nur einem Entwickler.
  • Es kann auch ein Problem mit der alten, schlecht gewarteten und nie dokumentierten Codebasis sein , bei der die einzigen Entwickler, die sich die Konsequenzen einer Änderung wirklich vorstellen können, das Unternehmen vor Jahren verlassen haben. Hier hat das Projekt gerade erst begonnen und der Entwickler verwendet keine Codebasis von irgendjemandem.

Was kann die Ursache für ein solches Problem in einer neuen, kleinen Codebasis sein, die von einem einzelnen Entwickler geschrieben wurde, der sich weiterhin auf seine Arbeit konzentriert ?

Was kann helfen?

  • Unit-Tests (gibt es keine)?
  • Richtige Architektur (ich bin mir ziemlich sicher, dass die Codebasis überhaupt keine Architektur hat und ohne vorläufige Überlegungen geschrieben wurde), die das gesamte Refactoring erfordert?
  • Paar-Programmierung?
  • Etwas anderes?

14
Ah, das gute alte Designmuster "Wellen des Scheiterns". :-)
Brian Knoblauch

1
Ich vergleiche es mit einer Blase in einem Kontaktbogen. Drücken Sie es nach unten, es erscheint woanders. Je besser meine Codierung wird,
desto

2
Nebenbei bemerkt, ich hatte genau das auf einem eingebetteten System. Ich habe einen Funktionsaufruf hinzugefügt, um ein Problem zu beheben. Dieser Funktionsaufruf war zu viel für den Stack (der Mikrocontroller hatte keine Stackoverflow-Erkennung) und so wurden einige zufällige Dinge in den Speicher geschrieben, die natürlich etwas völlig anderes kaputt machten. Also, dieses Ding KANN auf einer kleinen Codebasis mit nur einem Entwickler und guter Architektur passieren.
risingDarkness

... und das war ein Albtraum zu debuggen.
risingDarkness

Antworten:


38

Es hat nicht viel mit Fokus, Projektgröße, Dokumentation oder anderen Prozessproblemen zu tun. Solche Probleme sind normalerweise auf eine übermäßige Kopplung im Design zurückzuführen, wodurch es sehr schwierig ist, Änderungen zu isolieren.


15
dies kombiniert mit schlechten oder keinen Regressionstests
Ryathal

3
Richtig, @Ryathal, obwohl Regressionstests diese Art von Fehlern nicht verhindern, lassen Sie es Sie einfach früher wissen.
Karl Bielefeldt

Wenn Sie früh genug davon erfahren (z. B. wenige Minuten nach dem Erstellen der Bugs), können Sie Ihre Änderungen rückgängig machen und so tun, als wären sie nie geschehen.
BDSL

14

Eine der Ursachen kann eine enge Kopplung zwischen den Komponenten Ihrer Software sein: Wenn es keine einfachen, genau definierten Schnittstellen zwischen den Komponenten gibt, kann bereits eine kleine Änderung in einem Teil des Codes unerwartete Nebenwirkungen in anderen Teilen der Software hervorrufen Code.

Als Beispiel habe ich kürzlich an einer Klasse gearbeitet, die eine GUI-Komponente in meiner Anwendung implementiert. Wochenlang wurden neue Fehler gemeldet, ich habe sie behoben, und an anderer Stelle traten neue Fehler auf. Mir wurde klar, dass diese Klasse zu groß geworden war, zu viele Dinge tat und viele Methoden davon abhingen, dass andere Methoden in der richtigen Reihenfolge aufgerufen wurden, um richtig zu funktionieren.

Anstatt die letzten drei Fehler zu beheben, habe ich einiges überarbeitet: Die Komponente wurde in eine Hauptklasse plus MVC-Klassen (drei zusätzliche Klassen) aufgeteilt. Auf diese Weise musste ich den Code in kleinere, einfachere Teile aufteilen und klarere Schnittstellen definieren. Nach dem Refactoring wurden alle Bugs behoben und keine neuen Bugs gemeldet.


7

Es ist leicht für einen Fehler, einen anderen zu maskieren. Angenommen, Fehler "A" führt dazu, dass die falsche Funktion aufgerufen wird, um Eingaben zu verarbeiten. Wenn der Fehler "A" behoben ist, wird plötzlich die richtige Funktion aufgerufen, die noch nie getestet wurde.


5

Nun, die unmittelbare Ursache ist, dass zwei Fehler richtig oder zumindest nicht offensichtlich falsch sind. Ein Teil des Codes gleicht das fehlerhafte Verhalten des anderen Teils aus. Oder wenn der erste Teil als solcher nicht "falsch" ist, gibt es eine ungeschriebene Vereinbarung zwischen den beiden Teilen, die verletzt wird, wenn der Code geändert wird.

Angenommen, die Funktionen A und B verwenden für eine bestimmte Menge eine auf Null basierende Konvention, sodass sie ordnungsgemäß zusammenarbeiten. C verwendet jedoch eine Konvention. Sie können A "reparieren", um mit C zu arbeiten, und dann ein Problem mit B feststellen.

Das tiefere Problem ist das Fehlen einer unabhängigen Überprüfung der Richtigkeit der einzelnen Teile. Unit-Tests sollen dies ansprechen. Sie dienen auch als Spezifikation der richtigen Eingaben. Zum Beispiel würde eine gute Reihe von Tests deutlich machen, dass die Funktionen A und B eine 0-basierte Eingabe und eine 1-basierte Eingabe erwarten.

Die richtigen Spezifikationen können auch auf andere Weise erstellt werden, von offiziellen Dokumenten bis hin zu guten Kommentaren im Code, je nach den Anforderungen des Projekts. Der Schlüssel ist zu verstehen, was jede Komponente erwartet und was sie verspricht, damit Sie Inkonsistenzen finden können.

Gute Architektur hilft bei dem Problem, den Code zu verstehen, und erleichtert dies. Paarprogrammierung ist hilfreich, um Fehler zu vermeiden oder sie schneller zu finden.

Hoffe das hilft.


5

Enge Kopplung, fehlende Tests, dies sind wahrscheinlich die häufigsten Schuldigen. Grundsätzlich geht es nur um schlechte Standards und Vorgehensweisen. Ein weiterer Grund ist der falsche Code, der es schafft, mit korrektem Verhalten für eine Weile Glück zu haben. Betrachten Sie einen memcpyFehler von Linus Torvalds, bei dem das Ändern seiner Implementierung Fehler in Clients aufdeckte (nicht verursachte), die memcpyan Orten eingesetzt wurden, an denen sie memmovemit überlappenden Quellen und Zielen hätten arbeiten sollen.


4

Es klingt so, als ob diese "neuen" Bugs keine "neuen" Bugs sind. Sie waren einfach kein Problem, bis der andere Code, der kaputt war, tatsächlich behoben wurde. Mit anderen Worten, Ihr Kollege merkt nicht, dass er die ganze Zeit tatsächlich zwei Bugs hatte. Wenn der Code, der sich nicht als fehlerhaft herausstellt, nicht fehlerhaft war, wäre er auch dann nicht fehlgeschlagen, wenn der andere Teil des Codes tatsächlich repariert worden wäre.

In beiden Fällen kann ein besser automatisiertes Testprogramm hilfreich sein. Es hört sich so an, als müsste Ihr Kollege die aktuelle Codebasis einem Komponententest unterziehen. Bei zukünftigen Regressionstests wird überprüft, ob vorhandener Code weiterhin funktioniert.


0

Verbessern Sie die Breite Ihres automatisierten Testprogramms. Führen Sie IMMER alle Tests durch, bevor Sie Codeänderungen vornehmen. Auf diese Weise erkennen Sie die schädlichen Auswirkungen Ihrer Änderungen.


0

Ich bin gerade auf dieses Problem gestoßen, als ein Test nicht korrekt war. Der Test überprüfte einen bestimmten Berechtigungsstatus, der korrekt war. Ich habe den Code aktualisiert und den Berechtigungstest ausgeführt. Es funktionierte. Dann habe ich alle Tests durchgeführt. Alle anderen Tests, bei denen die geprüfte Ressource verwendet wurde, sind fehlgeschlagen. Ich habe den Test und die Berechtigungsprüfung korrigiert, aber zuerst gab es ein bisschen Panik.

Es kommt auch zu inkonsistenten Spezifikationen. Dann ist es fast garantiert, dass das Beheben eines Fehlers einen anderen erzeugt (aufregend, wenn dieser bestimmte Teil der Spezifikation erst später im Projekt ausgeführt wird).


0

Stellen Sie sich vor, Sie haben ein vollständiges Produkt. Wenn Sie dann etwas Neues hinzufügen, scheint alles in Ordnung zu sein, aber Sie haben etwas anderes kaputt gemacht, was von einem Code abhängt, den Sie ändern, damit das neue Feature funktioniert. Auch wenn Sie keinen Code ändern , sondern nur vorhandene Funktionen erweitern, kann dies zu Problemen führen.

Im Grunde haben Sie sich selbst fast geantwortet:

  • lose Kopplung
  • Mangel an Tests

Lernen Sie einfach, das TDD-Prinzip anzupassen (zumindest für neue Funktionen) und versuchen Sie, alle möglichen Zustände zu testen, die auftreten können.

Pair-Programmierung ist großartig, aber nicht immer "verfügbar" (Zeit, Geld, beides ...). Aber auch Code-Überprüfungen (zum Beispiel von Ihren Kollegen) einmal pro Tag / Woche / eine Reihe von Commits helfen sehr, insbesondere wenn die Überprüfung die Testsuite enthält. (Ich finde es schwierig, keine Fehler in Testsuiten zu schreiben ... manchmal muss ich den Test intern testen (Sanity Check) :)).


0

Angenommen, Entwickler A hat Code mit einem Fehler geschrieben. Der Code stimmt nicht genau mit dem überein, was er tun soll, aber etwas anderes. Entwickler B hat Code geschrieben, der darauf beruhte, dass der Code von A genau das tat, was er tun sollte, und der Code von B funktioniert nicht. B untersucht, findet das falsche Verhalten im Code von A und behebt es.

In der Zwischenzeit funktionierte der Code von Entwickler C nur richtig, weil er sich auf das falsche Verhalten von Code A stützte. Der Code von A ist jetzt korrekt. Und der Code von C funktioniert nicht mehr. Das heißt, wenn Sie Code reparieren, müssen Sie sehr sorgfältig prüfen, wer diesen Code verwendet und wie sich ihr Verhalten mit dem festen Code ändert.

Ich hatte eine andere Situation: In einer Situation X hat sich ein Code schlecht verhalten und ein Feature vollständig funktionsunfähig gemacht. Daher habe ich das Fehlverhalten geändert und das Feature funktionsfähig gemacht. Der unglückliche Nebeneffekt war, dass das gesamte Feature in Situation X erhebliche Probleme hatte und überall versagte - dies war niemandem bekannt, da die Situation noch nie zuvor aufgetreten war. Nun, das ist hart.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.