Schreiben von robustem Code im Vergleich zu Überentwicklung

33

Woher weißt du, dass du den robustesten Code schreibst, der ohne Überarbeitung möglich ist?

Ich denke zu viel über jeden möglichen Weg nach, den mein Code einschlagen kann, und es fühlt sich manchmal wie Zeitverschwendung an. Ich denke, es hängt von der Art des Programms ab, das Sie schreiben, aber ich möchte nicht zu viel Zeit in Anspruch nehmen, wenn ich Situationen in Betracht ziehe, die niemals eintreten werden.

Fran Sevillano
quelle
2
Code es in Agda2
SK-Logik
Ein konkretes Beispiel würde Ihnen dabei sehr helfen, Ihre Meinung zu vertreten. :)
João Portela
Kann ich nur überprüfen, ob Sie wirklich nach der Robustheit fragen, dh nach der Fähigkeit eines Systems, bei ungültigen Eingaben oder stressigen Umgebungsbedingungen weiterzuarbeiten? Einige Antworten scheinen zu glauben, dass Sie von Erweiterbarkeit sprechen.
DJClayworth
Ich arbeite unter verrückten Fristen und es ist für Demo-Ware, so dass ich mich ohne Perfektionslähmung glücklich und schnell davonhacken kann.
Job
1
Hier ist ein Artikel, der über das Thema spricht: code-tag.com/2017/04/02/…
San

Antworten:

39

Woher weißt du, dass du den robustesten Code schreibst, der ohne Überarbeitung möglich ist?

Was halten Sie von robustem Code? Code, der bereits zukunftssicher und so leistungsfähig ist, dass er mit jeder Situation umgehen kann? Falsch, niemand kann die Zukunft vorhersagen! Und wieder falsch, denn es wird ein kompliziertes, unhaltbares Durcheinander.

Ich verfolge verschiedene Prinzipien: In erster Linie YAGNI (noch) und KISS , damit ich keinen unnötigen Code schreibe. Das beugt auch Überentwicklungen wirksam vor. Ich überarbeite die Anwendung, wenn Erweiterungen benötigt werden. Mit modernen Refactoring-Tools können Sie ganz einfach Schnittstellen erstellen und Implementierungen später bei Bedarf austauschen.

Dann versuche ich, den Code, den ich schreibe, so robust wie möglich zu gestalten. Dazu gehört, möglichst viele Pfade, die das Programm nehmen kann (und auch Zustände), und ein wenig spartanische Programmierung zu eliminieren . Eine große Hilfe sind "atomare" Funktionen / Methoden, die sich nicht auf externe Zustände stützen oder das Programm zumindest nicht in einem inkonsistenten Zustand belassen, wenn sie fehlschlagen. Wenn Sie das gut machen, ist es auch sehr unwahrscheinlich, dass Sie jemals mit Spaghetti-Code enden, und es ist auch ein Segen für die Wartbarkeit. Beim objektorientierten Entwurf sind die SOLID- Prinzipien auch ein guter Leitfaden für robusten Code.

Ich habe wirklich herausgefunden, dass Sie häufig die Komplexität reduzieren können, beispielsweise durch kombinatorische Explosionen von Programmpfaden oder -zuständen, indem Sie sich gründlich überlegen, wie Sie diesen Pfad als möglichst geraden Pfad gestalten können. Versuchen Sie, die möglichen Kombinationen auf ein Minimum zu beschränken, indem Sie die beste Reihenfolge Ihrer Unterprogramme auswählen und sie für diesen Zweck entwerfen.

Robuster Code ist meistens einfacher und sauberer Code, aber Einfachheit ist eine Eigenschaft, die nicht immer leicht zu erreichen ist. Dennoch solltest du danach streben. Schreiben Sie immer nur den einfachsten Code und erhöhen Sie die Komplexität nur, wenn Sie keine andere Wahl haben.

Einfachheit ist robust, Komplexität ist fragil.

Komplexität tötet.

Falke
quelle
2
Weil es nicht viele Klassen, Fabriken und Abstraktionen gibt. Es ist ein Paradox, aber manche Leute mögen das Zeug. Keine Ahnung warum.
Coder
5
Das ist Sparta!!!
Tom Squires
4
Leute, die das seit zwanzig Jahren nicht mehr machen, wissen einfach nicht, wie viel Komplexität Sie umbringen kann. Sie denken, sie sind so schlau. Sie sind dumm, nicht schlau. Diese Komplexität wird dich umbringen.
PeterAllenWebb
1
Bei Robustheit geht es nicht um Zukunftssicherheit, sondern darum, mit ungültigen Eingaben oder in stressigen Umgebungen weiterzuarbeiten - Code Complete p464.
DJClayworth
5
Sofern der Fragesteller "robust" nicht in einem anderen Sinne verwendet als dem, den ich verstehe, beantworten Sie eine andere Frage. Er fragt nicht, "ob ich Code für zukünftige Anforderungen verwenden soll", sondern "mit welchen ungewöhnlichen Eingabefällen ich umgehen soll". Keiner von YAGNI, KISS und SOLID ist relevant. Müssen Sie eine Million Benutzer zulassen, die gleichzeitig versuchen, sich anzumelden? Was passiert, wenn ein Anmeldename mit einem Backslash beginnt? Keine dieser Fragen wird von YAGNI beantwortet.
DJClayworth
8

Ich versuche ein Gleichgewicht zu halten und konzentriere mich auf

  • Behandlung aller möglichen Ausführungspfade in den vorhandenen Anwendungsfällen (dies ist der Teil "Robustheit"),
  • Aktivieren von Funktionen / Anforderungen Ich bin mir ziemlich sicher, dass dies in absehbarer Zeit geschehen wird
  • Dinge, die ich aus Erfahrung weiß, die für die langfristige Wartbarkeit der Codebasis erforderlich sind (dh, den Code sauber und überprüfbar halten).

Es ist ein unscharfer Grenzbereich - manchmal schaffe ich es, nicht benötigte Arbeiten zu erledigen, manchmal schaffe ich es nicht, etwas zu tun, was sich später als notwendig herausstellt. Wenn die Fehler nicht groß sind, geht es mir gut. Ich bemühe mich jedenfalls, aus meinen Fehlern zu lernen.

Péter Török
quelle
Ein Beispiel wäre hier großartig, wenn alle möglichen Ausführungspfade in den vorhandenen Anwendungsfällen behandelt würden
CodeYogi
5

Der Unterschied zwischen robuster und Überentwicklung ist der Unterschied zwischen dem angemessenen Umgang mit allen möglichen Anwendungsfällen, auch den bizarren und Rand-Anwendungsfällen, die NICHT vorkommen SOLLTEN. Wenn ich es anmutig sage, betrete der Benutzer einen bizarren Ausnahmefall oder gerät in eine Situation, in der eine nicht unterstützte oder nicht spezifizierte Funktion erforderlich ist, die nicht definiert wurde, und der Code wird ordnungsgemäß ohne Absturz beendet oder informiert den Benutzer über nicht unterstützte Funktionen.

Overengineering hingegen kann in den Bereich der vollständigen Implementierung von Funktionen fallen, die nicht benötigt oder angefordert wurden (einige Funktionen werden vom Kunden zwar benötigt, wurden aber nie angefordert!) ODER es kann definiert werden, indem ein zu komplexes Design oder ein zu komplexes Design abgeleitet wird Code, um ein relativ einfaches Problem zu behandeln.

maple_shaft
quelle
4

1) Anforderungen abrufen.

2) Schreiben Sie den Mindestcode, um die Anforderungen zu erfüllen. Wenn etwas nicht eindeutig ist, machen Sie eine fundierte Vermutung. Wenn es sich um Super - mehrdeutig, gehen Sie zu 1 zurück.

3) Zum Testen senden.

4) Wenn die Tester es für gut halten, dokumentieren Sie die Genehmigung. Wenn etwas nicht stimmt, gehe zurück zu 1.

Konzentrieren Sie sich darauf, Tests zu bestehen, ohne sie vorherzusagen. Wenn Sie keine Tester haben ... holen Sie sich Tester! Sie sind nicht nur für die Überprüfung der Code-Korrektheit wichtig, sondern für den gesamten Entwicklungsprozess.

Morgan Herlocker
quelle
1
+1 für Konzentration auf das Bestehen von Tests, nicht auf das Vorhersagen von Tests, wie auch immer von vielen Entwicklern wie mir erwartet wird, dass sie beides tun, jedoch in Ermangelung starker Geschäftsanalysten.
maple_shaft
@maple_shaft - Sehr wahr. Der Punkt ist, dass diese Probleme aufgrund der Inkompetenz eines anderen auftreten. Sich über den Job eines anderen Gedanken zu machen, ist der Weg zum Burnout. Wenn meine Firma dumm genug wäre, die Forderungen des Monats zu erfüllen, wäre ich nicht zu enttäuscht, wenn es nicht gut ausgehen würde. Das Definieren von Anforderungen besteht in der Regel nur darin, zu beschreiben, was Sie täglich tun, damit es automatisiert werden kann. Wenn keiner der Mitarbeiter dazu in der Lage ist, könnte das Unternehmen in Schwierigkeiten geraten.
Morgan Herlocker
3

Halten Sie zunächst die Daten so weit wie möglich normalisiert (nicht redundant). Wenn die Daten vollständig normalisiert sind, kann keine einzelne Aktualisierung der Daten zu Inkonsistenzen führen.

Sie können die Daten nicht immer normalisieren, mit anderen Worten, Sie können Redundanz möglicherweise nicht beseitigen. In diesem Fall kann es zu inkonsistenten Zuständen kommen. Das Ding, das zu tun ist, ist , die Inkonsistenz zu tolerieren und sie regelmäßig mit einer Art Programm zu reparieren, das darüber hinwegfegt und es repariert.

Es besteht eine starke Tendenz, zu versuchen, die Redundanz mithilfe von Benachrichtigungen eng zu verwalten. Diese sind nicht nur schwer auf ihre Richtigkeit zu überprüfen, sondern können auch zu enormen Ineffizienzen führen. (Ein Teil der Versuchung, Benachrichtigungen zu schreiben, entsteht, weil sie in OOP praktisch gefördert werden.)

Im Allgemeinen ist alles, was von der zeitlichen Abfolge von Ereignissen, Nachrichten usw. abhängt, anfällig und erfordert Tonnen von defensiver Codierung. Ereignisse und Meldungen sind für redundante Daten charakteristisch, da sie Änderungen von einem Teil zum anderen übermitteln, um Inkonsistenzen zu vermeiden.

Wie gesagt, wenn Sie über Redundanz verfügen müssen (und die Chancen stehen ziemlich gut), ist es am besten, wenn Sie in der Lage sind, a) dies zu tolerieren und b) es zu reparieren. Wenn Sie versuchen, Inkonsistenzen nur durch Nachrichten, Benachrichtigungen, Trigger usw. zu verhindern, wird es Ihnen sehr schwer fallen, sie robust zu machen.

Mike Dunlavey
quelle
3
  • Schreiben Sie zur Wiederverwendung.
  • schreiben Sie Tests. trivial, nichttrivial, einige absurd komplexe, um zu sehen, wie es unter solchen Bedingungen gehandhabt wird. Tests helfen Ihnen auch dabei, die Form der Schnittstelle zu bestimmen.
  • Schreiben Sie das Programm so, dass es schwer versagt (z. B. Behauptung). Mein Code hat eine Menge Wiederverwendung und ich teste eine Menge Fälle - es gibt mehr Fehlerprüfung / -behandlung als die tatsächliche Implementierung (basierend auf der Anzahl der Zeilen).
  • Wiederverwendung.
  • Beheben Sie sofort Probleme.
  • lernen und bauen aus erfahrung.

Fehler werden auf dem Weg dorthin auftauchen, aber sie werden (zum Glück) lokalisiert und werden (in den meisten Fällen) sehr früh im Test auftauchen. Der andere Vorteil der Wiederverwendung besteht darin, dass der Kunde / Anrufer den größten Teil der Fehlerprüfung / des Gerüsts mit dem von der Implementierung mitgebrachten Aufwand einsparen kann.

Ihre Tests definieren dann die Fähigkeiten und die Robustheit Ihres Programms. Fügen Sie weitere Tests hinzu, bis Sie mit den Erfolgsquoten und Eingaben zufrieden sind. nach Bedarf verbessern, erweitern und verstärken.

Justin
quelle
2

Ich mache diese Unterscheidung, indem ich Code mit genau definierten, aber nicht unbedingt optimalen Verhaltensweisen für sehr unwahrscheinliche Ausführungspässe schreibe. Wenn ich zum Beispiel ziemlich sicher bin (nachgewiesen, aber nicht getestet), dass eine Matrix eindeutig ist, füge ich eine Zusicherung oder Ausnahme in das Programm ein, um den Status zu testen, schreibe aber keinen eigenen Codepfad dafür. Dadurch wird das Verhalten definiert, aber suboptimal.

Thiton
quelle
2

Robustheit: Der Grad, in dem ein System bei ungültigen Eingaben oder stressigen Umgebungsbedingungen weiter funktioniert. (Code Complete 2, S. 464)

Die wichtige Frage hierbei ist, wie wichtig Robustheit für Sie ist. Wenn Sie Facebook sind, ist es sehr wichtig, dass Ihre Website weiterhin funktioniert, wenn jemand Sonderzeichen in die Eingabe eingibt, und dass Ihr Server nicht ausfällt, wenn 100 Millionen Benutzer gleichzeitig angemeldet sind. Wenn Sie ein Skript schreiben, um eine allgemeine Operation auszuführen, die nur Sie ausführen, ist Ihnen das egal. Dazwischen gibt es viele Levels. Ein Urteil darüber zu fällen, wie viel Robustheit Sie benötigen, ist eine der wichtigen Fähigkeiten, die ein Entwickler erlernen sollte.

Das Prinzip von YAGNI gilt für das Hinzufügen von Funktionen, die ein Programm möglicherweise benötigt. Dieses Prinzip gilt jedoch nicht für die Robustheit. Programmierer neigen dazu, die Wahrscheinlichkeit zu überschätzen, dass eine bestimmte zukünftige Erweiterung benötigt wird (insbesondere wenn es eine coole ist), aber sie unterschätzen die Wahrscheinlichkeit, dass etwas schief geht. Wenn sich herausstellt, dass eine ausgelassene Funktion später benötigt wird, kann der Programmierer sie später schreiben. Wenn sich herausstellt, dass eine ausgelassene Fehlerprüfung erforderlich ist, kann der Schaden verursacht werden.

Daher ist es eigentlich besser, sich auf die Suche nach ungewöhnlichen Fehlerzuständen zu begeben. Aber es gibt ein Gleichgewicht. Einige der Dinge, die in dieser Bilanz berücksichtigt werden müssen:

  • Wie oft kann dieser Fehler auftreten?
  • Was kostet es, wenn dieser Fehler auftritt?
  • Ist dies für den internen oder externen Gebrauch?

Vergessen Sie nicht, dass die Benutzer versuchen können und werden, Ihr Programm auf unerwartete Weise zu verwenden. Es ist besser, wenn etwas Vorhersehbares passiert, wenn sie es tun.

Verwenden Sie als letzte Verteidigungslinie die Zusicherung oder das Herunterfahren. Wenn etwas passiert, mit dem Sie nicht umgehen können, fahren Sie das Programm herunter. Das ist normalerweise besser, als dem Programm zu erlauben, etwas Unvorhersehbares zu tun.

DJClayworth
quelle