Effizienz

Erfahrene Programmierer können eine gute Architektur leicht von einer schlechten unterscheiden, aber wenn man sie bittet, sie in wenigen Worten zu beschreiben, wird ihnen das wahrscheinlich nicht gelingen. Es gibt kein einheitliches Kriterium für gute Architektur und keine einheitliche Definition.

Wenn man jedoch darüber nachdenkt, kann man eine Reihe von Kriterien aufschreiben, die eine gute Architektur erfüllen sollte. Eine gute Architektur ist zunächst einmal eine logische Architektur, die den Prozess der Entwicklung und Wartung eines Programms einfacher und effizienter macht.

Wenn ein Programm über eine gute Architektur verfügt, ist es immer leicht zu verstehen, wie es funktioniert und wo Code geschrieben werden muss. Ein gut strukturiertes Programm lässt sich einfacher ändern, testen, debuggen und entwickeln. Kluge Köpfe haben folgende Kriterien für gute Architektur formuliert:

  • Effizienz;
  • Flexibilität;
  • Erweiterbarkeit;
  • Skalierbarkeit;
  • Testbarkeit;
  • Wartbarkeit des Codes.

Systemeffizienz. Das Programm muss natürlich die gestellten Aufgaben lösen und seine Funktionen unter verschiedenen Bedingungen gut erfüllen. Es scheint, dass jedes Programm das tut, was es tun soll (sofern es geschrieben ist), aber oft ist dies überhaupt nicht der Fall.

Sie werden ständig auf Programme stoßen, die nicht das tun, was sie angeblich tun.

  • Libre Office ist ein vollständiger Ersatz für Microsoft Office (nicht wirklich);
  • Der Edge-Browser unterstützt alle Webstandards (nicht wirklich);
  • Der Bank liegt die Sicherheit der persönlichen Daten ihrer Nutzer am Herzen (eigentlich nicht).

Und auf Leistung, Zuverlässigkeit, zeitnahe Fehlerbehebungen oder die Veröffentlichung von Informationen über bekannte Schwachstellen haben wir uns noch nicht eingelassen.

Es ist klar, dass niemand perfekt ist, aber das Programm muss seine Hauptaufgaben lösen. Daher ohne Effizienz nirgendwo.

Flexibilität

Wichtiger als Effizienz ist meiner Meinung nach nur die Flexibilität. Jede Anwendung muss sich im Laufe der Zeit ändern, wenn sich Anforderungen ändern und neue hinzukommen. Je schneller und komfortabler Änderungen an der bestehenden Funktionalität möglich sind, je weniger Probleme und Fehler dadurch verursacht werden, desto flexibler ist die Systemarchitektur.

Sehr oft denken unerfahrene Programmierer/Architekten, dass sie eine ideale Architektur für aktuelle Aufgaben benötigen. Nein. Sie benötigen eine ideale Architektur für die Aufgaben, die in einem Jahr auf Sie zukommen. Da Sie die künftigen Aufgaben noch nicht kennen, sollten Sie wissen, was diese sein werden.

Es macht keinen Sinn, sie vorhersagen zu wollen, denn es wird immer etwas Unerwartetes geben. Sie müssen jedoch damit rechnen, dass solche Aufgaben auftreten. Versuchen Sie daher im Entwicklungsprozess zu bewerten, was erhalten wird, und zwar im Hinblick darauf, wie es geändert werden muss.

Fragen Sie sich: „Was passiert, wenn sich die aktuelle Architekturentscheidung als falsch herausstellt?“, „Wie viel Code wird geändert?“. Die Änderung eines Fragments des Systems sollte sich nicht auf die anderen Fragmente auswirken.

Architekturentscheidungen sollten nach Möglichkeit nicht in Stein gemeißelt werden und die Folgen architektonischer Fehler sollten angemessen begrenzt werden. „Gute Architektur ermöglicht es Ihnen, wichtige Entscheidungen zu VERZÖGERN“ (Bob Martin) und minimiert die „Kosten“ von Fehlern.

Einer dieser Ansätze ist die Aufteilung der Anwendung in Microservices: Es ist einfach, die bereits vorhandene Logik in einzelne Teile aufzuteilen. Das größte Problem besteht jedoch darin, künftige Änderungen an einem Dutzend Diensten gleichzeitig vorzunehmen, um eine kleine Funktion zu implementieren.

Skalierbarkeit

Unter Skalierbarkeit versteht man die Möglichkeit, die Entwicklungszeit zu verkürzen, indem dem Projekt neue Personen hinzugefügt werden. Die Architektur sollte eine Parallelisierung des Entwicklungsprozesses ermöglichen, sodass viele Personen gleichzeitig am Programm arbeiten können.

Es scheint, dass diese Regel von selbst ausgeführt wird, aber in der Praxis ist alles genau das Gegenteil. Es gibt sogar ein äußerst beliebtes Buch, The Mythical Man-Month , das erklärt, warum sich die Entwicklungszeit verlängert, wenn neue Leute zu einem Projekt hinzugefügt werden.

Erweiterbarkeit

Erweiterbarkeit ist die Fähigkeit, einem System neue Funktionen und Entitäten hinzuzufügen, ohne seine Kernstruktur zu zerstören. In der Anfangsphase ist es sinnvoll, nur die grundlegende und notwendigste Funktionalität in das System einzubauen.

Das ist das sogenannte YAGNI-Prinzip – du wirst es nicht brauchen , „du wirst es nicht brauchen“. Gleichzeitig sollte die Architektur es Ihnen ermöglichen, bei Bedarf problemlos zusätzliche Funktionalitäten zu erweitern. Und damit die Einführung der wahrscheinlichsten Änderungen den geringsten Aufwand erfordert.

Die Anforderung, dass die Architektur des Systems flexibel und erweiterbar (also veränderbar und entwicklungsfähig) sein muss, ist so wichtig, dass sie sogar als separates Prinzip formuliert wird – das „Offen/Geschlossen-Prinzip . Das Open-Closed-Prinzip ist das zweite der fünf SOLID-Prinzipien: Softwareeinheiten (Klassen, Module, Funktionen) sollten für Erweiterungen offen, für Änderungen jedoch geschlossen sein .

Mit anderen Worten: Es sollte möglich sein, das Verhalten des Systems zu ändern und zu erweitern, ohne bestehende Teile des Systems neu zu schreiben .

Dies bedeutet, dass die Anwendung so gestaltet sein sollte, dass eine Änderung ihres Verhaltens und das Hinzufügen neuer Funktionen durch das Schreiben von neuem Code (Erweiterungen) erreicht werden kann, ohne dass vorhandener Code geändert werden muss.

In diesem Fall führt die Entstehung neuer Anforderungen nicht zu einer Änderung der bestehenden Logik, sondern kann vor allem durch deren Erweiterung umgesetzt werden. Dieses Prinzip ist die Grundlage der „Plug-in-Architektur“ (Plugin-Architektur). Die Techniken, mit denen dies erreicht werden kann, werden später besprochen.

Erinnern Sie sich an Servlets und Filter? Warum wurden Filter benötigt, und zwar sogar mit separaten Schnittstellen, wenn tatsächlich dieselbe Logik mithilfe von Servlets implementiert werden könnte?

Erst die Erfindung des Filterkonzepts (Service-Servlets) ermöglichte die Verlagerung verschiedener Servicefunktionen auf eine separate Ebene. Und in Zukunft war es bei der Änderung des Verhaltens von Filtern nicht mehr erforderlich, die Servlets zu ändern.

Vor der Erfindung von Filtern befand sich die gesamte Dienstlogik, die für die Umleitung von Anforderungen verantwortlich war, in den Servlets selbst. Und oft würde eine kleine Änderung in der Logik dazu führen, dass alle Servlets durchgegangen werden müssen und an allen verschiedene Änderungen vorgenommen werden müssen.

Testbarkeit

Wenn Sie ein Java-Backend-Entwickler sind, stellen Ihre Serveranwendungen häufig eine Reihe von Methoden als REST-API bereit. Und um zu überprüfen, ob alle Ihre Methoden wie vorgesehen funktionieren, müssen sie durch Tests abgedeckt werden.

Im Allgemeinen ist die API-Testabdeckung ein guter Stil. Damit können Sie sicherstellen, dass Ihre API wirklich das tut, was sie tun soll. Und was noch wichtiger ist: Sie können Änderungen an der Serverlogik vornehmen und ganz einfach überprüfen, ob Sie nicht versehentlich etwas kaputt gemacht haben .

Sobald Sie mit dem Schreiben von Tests beginnen, werden Sie feststellen, dass der meiste Code überhaupt nicht getestet werden kann: private Methoden, starke Kopplung, statische Klassen und Variablen.

„Warum brauchen wir Tests, wenn der Code funktioniert?“, wird sich der Anfänger fragen.

„Warum brauchen wir funktionierenden Code, wenn er nicht getestet werden kann?“, wird der Profi fragen.

Einfach zu testender Code enthält weniger Fehler und ist zuverlässiger. Aber Tests verbessern nicht nur die Codequalität. Fast alle Entwickler kommen irgendwann zu dem Schluss, dass die Forderung nach „guter Testbarkeit“ auch eine leitende Kraft ist, die automatisch zu gutem Design führt.

Hier ist ein Zitat aus dem Buch Ideal Architecture: „Verwenden Sie das Prinzip der „Testbarkeit“ einer Klasse als „Lackmustest“ für gutes Klassendesign. Selbst wenn Sie keine einzige Zeile Testcode schreiben, beantworten Sie diese Frage mit 90 % % der Fälle werden dazu beitragen, zu verstehen, wie alles mit seinem Design gut oder schlecht ist.

Es gibt eine ganze Methodik zur Entwicklung von Programmen auf Basis von Tests, die als Test-Driven Development (TDD) bezeichnet wird. Das ist natürlich das andere Extrem: Schreiben Sie Code, bevor Sie Code schreiben.

Wartbarkeit des Codes

In der Regel arbeiten viele Leute am Programm – einige gehen, neue kommen. Die durchschnittliche Arbeitszeit eines Programmierers in einem IT-Unternehmen beträgt eineinhalb Jahre. Wenn Sie also zu einem Projekt kamen, das 5 Jahre alt ist, dann haben nur 20 % Ihrer Kollegen von Anfang an daran mitgearbeitet.

Es ist sehr schwierig, ein Programm zu pflegen und weiterzuentwickeln, das andere geschrieben haben. Auch wenn das Programm bereits geschrieben ist, ist es oft notwendig, es weiter zu pflegen: Fehler beheben und kleinere Korrekturen vornehmen. Und oft muss dies von Leuten gemacht werden, die nicht an der Erstellung beteiligt waren.

Daher sollte eine gute Architektur es neuen Leuten relativ einfach und schnell ermöglichen, das System zu verstehen . Das Projekt muss sein:

  • Gut strukturiert.
  • Keine Duplikate enthalten.
  • Haben Sie gut formatierten Code.
  • Es ist wünschenswert, eine Dokumentation beizufügen.
  • Es ist notwendig, Standardlösungen und vertraute Lösungen für Programmierer anzuwenden.

Sie können das Projekt, an dem Sie arbeiten, ganz einfach anhand eines 5-Punkte-Systems bewerten . Zählen Sie einfach zwei Punkte für jede dieser Anforderungen . Und wenn Sie 5 oder mehr erreichen, haben Sie Glück.

Programmierer haben sogar den Grundsatz der geringsten Überraschung : Je exotischer das System, desto schwieriger ist es für andere zu verstehen. Normalerweise wird es in Bezug auf die Benutzeroberfläche verwendet, ist aber auch auf das Schreiben von Code anwendbar.