Du wirst das oder eine Geschichte darüber hassen, wie gut Code aussehen sollte

Wie viele neue sind kaputt und werden auf der Suche nach dem richtigen Code immer noch kaputt sein?  Dies ist die Zeit gekommen und ich wurde daran gehört :)


Guten Tag an alle. Vor einiger Zeit sprach ich mit Studenten über das Thema „Was erwarten wir von einem guten Code?“ Und beschloss, ihn hier zu duplizieren. Während der Übersetzung änderte sich der Text etwas, aber das Wesentliche blieb gleich. Der Artikel erwies sich als einfach (und sicherlich nicht vollständig), aber hier gibt es eine rationale Körnung.


Code sollte funktionieren


Offensichtliche Dinge bringen nicht weniger Probleme mit sich, weil sie offensichtlich sind. Viele Projekte scheitern nur, weil sich die Entwicklung vollständig von der Lösung realer Benutzerprobleme abgewandt hat


Lassen Sie uns darüber sprechen, was wir vom Code erwarten. Nun, für den Anfang sollte es funktionieren.


Das klingt natürlich offensichtlich, aber jeder von uns hat einmal versucht oder erfolgreich Code gestartet, der nicht einmal funktionieren würde. Lachen Sie also nicht. Der zweite Punkt - der Code sollte in falschen Situationen korrekt funktionieren. Das heißt, Fehler zu fangen. Aber gehen wir zurück zum ersten Punkt und sprechen ein wenig darüber.


In regelmäßigen Abständen erhalte ich eine Aufgabe, von der ich keine Ahnung habe, wie sie zu tun ist. Das heißt im Allgemeinen (ich versuche mich umzusehen und ständig etwas Neues auszuprobieren). Und hier wurde ich sofort angezogen, um einige Abstraktionen zu schreiben, eine Art Infrastruktur, um den Moment der wirklichen Arbeit zu verzögern. Das ist also falsch. Der Code sollte funktionieren. Ich weiß, dass ich mich wiederhole, aber das ist ein wichtiger Punkt. Wenn Sie nicht wissen, wie Sie das Problem lösen können, müssen Sie keine Schnittstellen, Module und das alles erstellen. Dies ist eine schlechte Idee, die am Ende Ihrer Zeit enden wird und Sie werden nirgendwo hingehen. Denken Sie daran, schlecht funktionierender Code ist um ein Vielfaches besser als guter, aber nicht funktionierender Code.


Es gibt eine alte Parabel über zwei Softwareunternehmen, die dasselbe Produkt hergestellt haben. Der erste hat es trotzdem getan, aber der erste ist auf den Markt gekommen, und der zweite hat alles perfekt gemacht und war spät dran. Infolgedessen gelang es der ersten Kampagne, den Markt zu erobern und das zweite Unternehmen zu kaufen. Es geht ein wenig um einen anderen, aber die Hauptidee ist immer noch dieselbe. Zuerst lösen wir das Problem, dann machen wir den Code schön.


Erstellen Sie im Allgemeinen zuerst einen funktionierenden Prototyp. Lassen Sie es lahm, krumm und unglücklich sein, aber wenn Sie gefragt werden, können Sie sagen, dass die Lösung bereits vorhanden ist, es bleibt, sie zu integrieren. Und schreiben Sie es neu, wie es sollte. Sie können versuchen, dies mit einer solchen Maxime auszudrücken - wenn Sie wissen, wie man die Aufgabe erledigt - machen Sie es gut. Wenn Sie es nicht wissen, lösen Sie es zuerst irgendwie.


Und es gibt einen wichtigen Punkt. Ich möchte, dass Sie verstehen. Dies ist kein Aufruf, schlechten Code zu schreiben. Der Code sollte gut sein. Dies ist ein Aufruf von First Thing First - zuerst funktioniert der Code, dann wird er umgestaltet.


Lassen Sie uns jetzt über Shit Happens sprechen. Wir haben also den Code, er funktioniert sogar. Vielmehr "funktioniert" es. Schauen wir uns ein einfaches Beispiel an:


public string Do(int x) { using (WebClient xx = new WebClient()) { return xx.DownloadString("https://some.super.url"); } } 

Dies ist ein großartiges Beispiel für "funktionierenden" Code. Warum? Da dies früher oder später nicht berücksichtigt wird, fällt unser Endpunkt ab. In diesem Beispiel wird der sogenannte Randfall - Borderline, "Bad Cases" - nicht berücksichtigt. Überlegen Sie sich beim Schreiben von Code, was möglicherweise schief geht. Tatsächlich spreche ich nicht nur über Fernanrufe, sondern über alle Ressourcen, die außerhalb Ihrer Kontrolle liegen - Benutzereingaben, Dateien, Netzwerkverbindungen, sogar die Datenbank. Alles, was brechen kann, wird im ungünstigsten Moment brechen, und das einzige, was Sie damit tun können, ist, so gut wie möglich darauf vorbereitet zu sein.


Leider sind nicht alle Probleme so offensichtlich. Es gibt eine Reihe von Problembereichen, bei denen fast garantiert Fehler auftreten. Arbeiten Sie beispielsweise mit dem Gebietsschema und mit Zeitzonen. Es ist Schmerz und schreit "alles funktioniert auf meiner Maschine." Sie müssen sie nur sorgfältig kennen und mit ihnen arbeiten.


Übrigens über Benutzereingaben. Es gibt ein sehr gutes Prinzip, das besagt, dass Benutzereingaben als falsch angesehen werden, bis etwas anderes bewiesen ist. Mit anderen Worten, überprüfen Sie immer, was der Benutzer eingegeben hat. Und ja, auch auf dem Server.


Gesamt:


  • Lassen Sie zuerst den Code funktionieren,
  • Dann mach ihn gut
  • Vergessen Sie nicht Randfälle und Fehlerbehandlung.

Lassen Sie uns nun über die Codeunterstützung sprechen


Support ist ein komplexes Konzept, aber ich würde hier drei Komponenten einschließen: Der Code sollte leicht zu lesen, leicht zu ändern und konsistent sein.


Wer schreibt die Kommentare auf Russisch? Niemand schreibt? Großartig Im Allgemeinen ist eines der Probleme der nicht englische Code. Nicht so. Ich hatte einen Code mit Klassen in Norwegisch und konnte ihre Namen einfach nicht aussprechen. Es war traurig. Offensichtlich wird die Unterstützung eines solchen Codes (für Nicht-Norweger) keine triviale Aufgabe sein. Das ist aber selten.


Bei der Lesbarkeit geht es im Allgemeinen um Benennung und Struktur. Die Namen von Entitäten - Klassen, Methoden, Variablen - sollten einfach, lesbar und bedeutungsvoll sein. Nehmen Sie unser vorheriges Beispiel.


 public string Do(int x) { using (WebClient xx = new WebClient()) { return xx.DownloadString("https://some.super.url"); } } 

Können Sie verstehen, was die Do-Methode trotz der Implementierung tut? Kaum. Ähnliches gilt für Variablennamen. Um zu verstehen, welche Art von xx-Objekt Sie benötigen, müssen Sie nach seiner Deklaration suchen. Dies nimmt uns Zeit und hindert uns daran zu verstehen, was im Allgemeinen im Code geschieht. Daher müssen Namen das Wesen der Handlung oder Bedeutung widerspiegeln. Wenn Sie beispielsweise die Do-Methode in GetUserName umbenennen, wird der Code etwas klarer und in einigen Fällen müssen wir uns die Implementierung nicht mehr ansehen. Ähnliches gilt für Variablennamen in den Formen x und xx. Es stimmt, es gibt allgemein akzeptierte Ausnahmen in Form von e für Fehler, i, k für Zykluszähler, n für Dimensionen und einige mehr.


Nehmen Sie zum Beispiel Ihren Code, den Sie vor einem Monat geschrieben haben, und versuchen Sie, ihn fließend zu lesen. Verstehst du, was dort los ist? Wenn ja, gratuliere ich Ihnen. Wenn nicht, haben Sie ein Problem mit der Lesbarkeit des Codes.


Im Allgemeinen gibt es so ein interessantes Zitat:


"In der Informatik gibt es nur zwei schwierige Dinge: die Ungültigmachung des Caches und das Benennen von Dingen." © Phil Karlton

In der Informatik gibt es nur zwei komplexe Dinge: Cache-Ungültigmachung und Benennung.


Erinnere dich an sie, wenn du deinen Wesen Namen gibst.


Die zweite Komponente von lesbarem Code ist seine Komplexität oder Struktur. Ich spreche von denen, die sechs verschachtelte ifas schreiben oder einen Rückruf in einen Rückruf innerhalb des Rückrufs schreiben möchten. JavaScript hat sogar einen Begriff namens Callback Hell .


Über perfekten Code zu sprechen ist einfach, aber das Schreiben ist etwas schwieriger. Das Wichtigste dabei ist, sich zumindest nicht selbst anzulügen. Wenn Ihr Code schlecht ist - nennen Sie ihn nicht Süßigkeiten, sondern nehmen Sie ihn und beenden Sie ihn


Haben Sie Mitleid mit Ihren Kollegen und sich selbst. Nach einer Woche müssen Sie diesen Code buchstäblich durchgehen, um etwas zu reparieren oder hinzuzufügen. Dies zu vermeiden ist nicht so schwierig:


  • Schreiben Sie kurze Funktionen,
  • Vermeiden Sie viel Verzweigung oder Verschachtelung.
  • Trennen Sie logische Codeblöcke in separate Funktionen, auch wenn Sie sie nicht wiederverwenden möchten.
  • Verwenden Sie Polymorphismus anstelle von if

Lassen Sie uns nun über eine kompliziertere Sache sprechen - dass guter Code leicht zu ändern ist. Wer kennt den Begriff Big Ball of Mud? Wenn jemand nicht vertraut ist - schauen Sie sich das Bild an.


In dieser Hinsicht mag ich Open Source im Allgemeinen sehr. Wenn Ihr Code für die ganze Welt offen ist, möchte ich ihn irgendwie zumindest normal machen.


Jedes Modul hängt von jedem Modul ab und Änderungen im Vertrag an einem Ort führen wahrscheinlich zum Aufkommen des Polarfuchses oder zumindest zu einem sehr langen Debug. Theoretisch ist der Umgang damit recht einfach: Reduzieren Sie die Abhängigkeit Ihres Codes von Ihrem eigenen Code. Je weniger Ihr Code über Implementierungsdetails weiß, desto besser ist es für ihn. In der Praxis ist dies jedoch viel komplizierter und führt zu einer erneuten Komplikation des Codes.


In Form von Tipps würde ich es so ausdrücken:


  • Verstecke deinen Code so tief wie möglich. Stellen Sie sich vor, Sie müssen es morgen manuell aus dem Projekt entfernen. Wie viele Stellen müssen Sie reparieren und wie schwer wird es sein? Versuchen Sie, diese Menge zu minimieren.
  • Vermeiden Sie zirkuläre Abhängigkeiten. Teilen Sie den Code in Ebenen (Logik, Schnittstelle, Datenzugriff) auf und stellen Sie sicher, dass die Ebenen der "unteren" Ebene nicht von den Ebenen der "oberen" Ebene abhängen. Beispielsweise sollte der Zugriff auf Daten nicht von der Benutzeroberfläche abhängen.
  • Gruppieren Sie die Funktionen in Module (Projekte, Ordner) und verbergen Sie die darin enthaltenen Klassen, wobei nur die Fassade und die Schnittstellen übrig bleiben.

Und zeichne. Zeichnen Sie einfach auf ein Blatt Papier, wie Ihre Daten von der Anwendung verarbeitet werden und welche Klassen dafür verwendet werden. Dies wird Ihnen helfen, die überkomplizierten Stellen zu verstehen, bevor alles irreparabel wird.


Und schließlich zur Einheitlichkeit. Versuchen Sie immer, den einheitlichen Stil des Teams einzuhalten, auch wenn es Ihnen falsch erscheint. Dies gilt für die Formatierung und Lösungsansätze. Verwenden Sie ~~ nicht zum Runden, auch wenn es schneller ist. Das Team wird es nicht zu schätzen wissen. Und wenn Sie anfangen, neuen Code zu schreiben, schauen Sie sich immer das Projekt an. Vielleicht wurde bereits etwas implementiert, das Sie benötigen, und Sie können es wiederverwenden.


Gesamt:


  • Richtige Benennung
  • Gute Struktur
  • Einheitlichkeit.

Der Code sollte sehr produktiv sein


Lass uns ein bisschen erkälten. Die nächste Anforderung, die wir berücksichtigen werden, ist, dass der Code ziemlich produktiv sein sollte.


Was meine ich mit dem Wort "genug"? Wahrscheinlich hat jeder gehört, dass vorzeitige Optimierungen böse sind, die Lesbarkeit beeinträchtigen und den Code komplizieren. Es stimmt. Es ist jedoch auch richtig, dass Sie Ihr Tool kennen und nicht darauf schreiben sollten, damit der Webmail-Client Core I7 zu 60% lädt. Sie sollten die typischen Probleme kennen, die zu Leistungsproblemen führen, und diese bereits beim Schreiben von Code vermeiden.


Kehren wir zu unserem Beispiel zurück:


 public string GetUserName(int userId) { using (WebClient http = new WebClient()) { return http.DownloadString("https://some.super.url"); } } 

Dieser Code hat ein Problem - synchrones Herunterladen über das Netzwerk. Dies ist eine E / A-Operation, die unseren Fluss einfriert, bis er ausgeführt wird. In Desktop-Anwendungen führt dies zu einer baumelnden Schnittstelle und in Serveranwendungen zu einer nutzlosen Speicherreservierung und Erschöpfung der Anzahl der Anforderungen an den Server. Wenn Sie nur solche Probleme kennen, können Sie bereits optimierten Code schreiben. Und in den meisten Fällen wird dies ausreichen.


Aber manchmal nein. Bevor Sie Code schreiben, müssen Sie daher im Voraus wissen, welche Anforderungen an die Leistung gestellt werden.


Lassen Sie uns nun über die Tests sprechen.


Dies ist nicht weniger heiliges Thema als das vorherige und vielleicht sogar mehr. Bei Tests ist alles kompliziert. Beginnen wir mit der Aussage - ich glaube, dass der Code durch eine angemessene Anzahl von Tests abgedeckt werden sollte.


Warum brauchen wir überhaupt Code Coverage und Tests? In einer idealen Welt werden sie nicht benötigt. In einer idealen Welt wird Code ohne Fehler geschrieben, und die Anforderungen ändern sich nie. Aber wir leben in einer weit von der Idealwelt entfernten Welt, daher benötigen wir Tests, um sicherzustellen, dass der Code korrekt funktioniert (es gibt keine Fehler) und dass der Code nach einer Änderung korrekt funktioniert. Dies ist der Vorteil, den uns Tests bringen. Andererseits garantieren selbst 100% (aufgrund der Besonderheiten der Berechnung von Metriken), die durch Tests abgedeckt werden, nicht, dass Sie absolut alles abgedeckt haben. Darüber hinaus verlangsamt jeder zusätzliche Test die Entwicklung, da Sie nach dem Ändern der Funktion auch die Tests aktualisieren müssen. Daher sollte die Anzahl der Tests angemessen sein und die Hauptschwierigkeit besteht darin, einen Kompromiss zwischen der Codemenge und der Stabilität des Systems zu finden. Diese Facette zu finden ist ziemlich schwierig und es gibt kein universelles Rezept dafür. Es gibt jedoch einige Tipps, die Ihnen dabei helfen können.


  • Behandeln Sie die Logik von Geschäftsanwendungen. Geschäftslogik ist alles, wofür eine Anwendung erstellt wird, und sie sollte so stabil wie möglich sein.
  • Decken Sie komplexe, kalkulierte Dinge ab. Berechnungen, Transformationen, komplexe Datenzusammenführungen. Eine, bei der es leicht ist, einen Fehler zu machen.
  • Bugs abdecken. Ein Fehler ist eine Flagge, die uns sagt, dass der Code hier anfällig war. Und dies ist ein guter Ort, um hier einen Test zu schreiben.
  • Decken Sie häufig wiederverwendeten Code ab. Es ist sehr wahrscheinlich, dass es regelmäßig aktualisiert wird, und wir müssen sicher sein, dass das Hinzufügen einer Sache die andere nicht kaputt macht.

Nicht ohne großen Bedarf abdecken


  • Ausländische Bibliotheken - Suchen Sie nach Bibliotheken, deren Code bereits durch Tests abgedeckt ist.
  • Infrastruktur - DI, automatische Zuordnung (wenn keine komplizierte Zuordnung vorliegt) und so weiter. Hierfür gibt es e2e- oder Integrationstests.
  • Triviale Dinge - Zuweisen von Daten zu Feldern, Weiterleiten von Anrufen usw. Sie werden mit ziemlicher Sicherheit viel nützlichere Orte finden, um sie mit Tests abzudecken.

Nun, das ist es im Grunde.


Zusammenfassend. Guter Code ist


  • Arbeitscode
  • Einfach zu lesen
  • Einfach zu wechseln
  • Schnell genug
  • Und in Tests in der richtigen Menge abgedeckt.

Viel Glück auf diese schwierige Weise. Und höchstwahrscheinlich wirst du das hassen. Wenn immer noch nicht, willkommen.


In der Tat ist dies eine falsche Welt voller Entdeckungen, Rechte und Ansichten.  Hier nur in der Nachbarschaft gibt es die Langeweile der Prügelstrafe, die Dunkelheit von mehr als 20 Jahre altem Code und ein Feld von Krücken für Regierungsbedingungen.  Auch würde ich es lieber die Haarlinienbrücke (C) nennen.  Suchen Sie nach Nachfragen, die Sie brennen werden.  Versuche etwas Gutes zu tun.  Machen Sie einfach die Welt zu einem besseren Ort und alles wird gut.

Source: https://habr.com/ru/post/de429732/


All Articles