Continuous Integration als Praxis, nicht Jenkins. Andrei Alexandrow

Continuous Integration als Praxis, nicht Jenkins. Andrei Alexandrow

Lassen Sie uns darüber sprechen, warum CI-Tools und CI zwei ganz verschiedene Dinge sind.

Welches Problem soll CI lösen, woher stammt die Idee, welche aktuellen Bestätigungen belegen, dass es funktioniert und wie erkennt man, dass man tatsächlich Praxis hat und nicht nur Jenkins installiert ist.

Die Idee, einen Vortrag über Continuous Integration zu halten, kam vor einem Jahr auf, als ich zu Vorstellungsgesprächen ging, um einen Job zu suchen. Ich hatte Gespräche mit 10-15 Unternehmen und nur eines konnte mir nachvollziehbar erklären, was CI ist und wie sie festgestellt haben, dass ihnen das fehlt. Die anderen redeten wirres Zeug über Jenkins 🙂 "Wir haben Jenkins, das macht Builds, CI!" In meinem Vortrag werde ich versuchen zu erklären, was Continuous Integration wirklich ist und warum Jenkins und ähnliche Tools nur sehr wenig damit zu tun haben.

Continuous Integration als Praxis, nicht Jenkins. Andrei Alexandrow

Und was kommt den meisten Leuten in den Sinn, wenn sie das Wort CI hören? Die meisten denken sofort an Jenkins, Gitlab CI, Travis usw.

Continuous Integration als Praxis, nicht Jenkins. Andrei Alexandrow

Selbst wenn wir googeln, werden diese Tools angezeigt.

Continuous Integration als Praxis, nicht Jenkins. Andrei Alexandrow

Wenn man fragt, ob sie bekannt sind, wird man sofort nach der Aufzählung der Tools hören, dass CI das ist, wenn bei einem Pull Request nach einem Commit ein Build durchgeführt und Tests ausgeführt werden.

Continuous Integration als Praxis, nicht Jenkins. Andrei Alexandrow

Kontinuierliche Integration bedeutet nicht, sich auf Werkzeuge oder das Erstellen von Build-Prozessen mit Tests in Branches zu konzentrieren! Kontinuierliche Integration ist eine Praxis, die eine sehr häufige Integration neuen Codes erfordert, und dazu ist es keineswegs notwendig, komplizierte Systeme wie Jenkins, GitLab usw. zu verwenden.

Continuous Integration als Praxis, nicht Jenkins. Andrei Alexandrow

Bevor wir uns damit beschäftigen, wie eine vollständige CI aussieht, lassen Sie uns zunächst in den Kontext der Menschen eintauchen, die dies erfunden haben, und das Problem nachfühlen, das sie zu lösen versuchten.

Continuous Integration als Praxis, nicht Jenkins. Andrei Alexandrow

Sie haben das Problem der Zusammenarbeit im Team gelöst!

Continuous Integration als Praxis, nicht Jenkins. Andrei Alexandrow

Schauen wir uns anhand von Beispielen an, mit welchen Schwierigkeiten Entwickler bei der Teamarbeit konfrontiert sind. Angenommen, wir haben ein Projekt, einen Master-Branch in Git und zwei Entwickler.

Continuous Integration als Praxis, nicht Jenkins. Andrei Alexandrow

Und sie haben so gearbeitet, wie es schon lange üblich ist. Sie haben sich eine Aufgabe in Jira geschnappt, einen Feature-Branch erstellt und Code geschrieben.

Continuous Integration als Praxis, nicht Jenkins. Andrei Alexandrow

Der eine hat das Feature schneller fertiggestellt und es in den Master gemerged.

Continuous Integration als Praxis, nicht Jenkins. Andrei Alexandrow

Der andere benötigte mehr Zeit, hat sich später gemerged und erhielt einen Konflikt. Jetzt verbringt der Entwickler anstelle von der Erstellung wichtiger Geschäftsfeatures seine Zeit und Energie mit der Lösung von Konflikten.

Continuous Integration als Praxis, nicht Jenkins. Andrei Alexandrow

Je komplizierter es ist, Ihre Funktion mit dem Hauptzweig zu verbinden, desto mehr Zeit investieren wir dafür. Und das ist noch ein relativ einfaches Beispiel. Hier gibt es nur zwei Entwickler. Stellen Sie sich vor, es sind 10, 15 oder sogar 100 Personen in einem Unternehmen, die in dasselbe Repository schreiben. Sie würden verrückt werden, all diese Konflikte zu lösen.

Continuous Integration als Praxis, nicht Jenkins. Andrei Alexandrow

Es gibt einen etwas anderen Fall. Wir haben einen Hauptzweig und mehrere Entwickler, die etwas tun.

Continuous Integration als Praxis, nicht Jenkins. Andrei Alexandrow

Jeder hat seinen eigenen Branch erstellt.

Continuous Integration als Praxis, nicht Jenkins. Andrei Alexandrow

Einer hat seinen Branch gemerged, alles gut, Aufgabe abgegeben.

Continuous Integration als Praxis, nicht Jenkins. Andrei Alexandrow

Der zweite Entwickler hat inzwischen seine Aufgabe fertiggestellt. Angenommen, er hat sie zur Überprüfung eingereicht. In vielen Unternehmen gibt es die Praxis der Überprüfung – einerseits eine gute und nützliche Praxis, andererseits bremst sie uns an manchen Stellen. Lassen wir das mal beiseite, aber es ist ein hervorragendes Beispiel, wohin eine komplizierte Geschichte mit der Überprüfung führen kann. Sie haben einen Pull-Request zur Überprüfung eingereicht. Der Entwickler hat nichts mehr zu tun. Was beginnt er zu tun? Er anfängt, andere Aufgaben zu übernehmen.

Continuous Integration als Praxis, nicht Jenkins. Andrei Alexandrow

In der Zwischenzeit hat der zweite Entwickler noch etwas gemacht.

Continuous Integration als Praxis, nicht Jenkins. Andrei Alexandrow

Der erste hat die dritte Aufgabe abgeschlossen.

Continuous Integration als Praxis, nicht Jenkins. Andrei Alexandrow

Und nach einer gewissen Zeit wurde sein Review getestet und er versucht, es zusammenzuführen. Und was passiert? Er stößt auf eine Menge Konflikte. Warum? Weil während sein Pull Request zur Überprüfung bereitstand, sich im Code bereits viel verändert hat.

Neben der Problematik der Konflikte gibt es auch das Thema Kommunikation. Solange Ihre Branch zur Überprüfung offen ist und auf etwas wartet, während Sie an einem Feature arbeiten, verlieren Sie den Überblick darüber, was sich sonst noch in der Codebasis Ihres Services ändert. Möglicherweise wurde das, was Sie gerade zu lösen versuchen, bereits gestern geklärt, und Sie könnten eine Methode wiederverwenden. Aber das werden Sie nicht sehen, weil Sie immer mit einer veralteten Branch arbeiten. Und diese veraltete Branch führt immer dazu, dass Sie Merge-Konflikte lösen müssen.

Das bedeutet, wenn wir im Team arbeiten, also nicht nur eine Person im Repository arbeitet, sondern 5-10 Personen, desto länger wir unseren Code nicht in den Master einfügen, desto mehr leiden wir darunter, dass schließlich etwas zusammengeführt werden muss. Und je mehr Konflikte wir haben und je älter die Version ist, mit der wir arbeiten, desto mehr Probleme haben wir.

Continuous Integration als Praxis, nicht Jenkins. Andrei Alexandrow

Gemeinsam etwas zu tun kann schmerzhaft sein! Wir stören uns gegenseitig immer wieder.

Continuous Integration als Praxis, nicht Jenkins. Andrei Alexandrow

Dieses Problem wurde vor über 20 Jahren angesprochen. Das erste Mal, dass ich von der Praxis der Continuous Integration gehört habe, fand ich im Zusammenhang mit extreme Programming.

Extreme Programming ist das erste agile Framework. Die Seite entstand 1996. Die Idee war, bestimmte Praktiken des Programmierens, Planens und anderer Bereiche zu nutzen, um die Entwicklung so flexibel wie möglich zu gestalten, damit wir schneller auf Änderungen und Anforderungen unserer Kunden reagieren können. Vor 24 Jahren begannen sie zu erkennen, dass, wenn man etwas sehr lange im Abseits macht, man mehr Zeit darauf verwendet, da es zu Konflikten kommt.

Continuous Integration als Praxis, nicht Jenkins. Andrei Alexandrow

Jetzt werden wir den Begriff "Continuous Integration" Wort für Wort analysieren. Wenn man wörtlich übersetzt, bedeutet es kontinuierliche Integration. Aber wie kontinuierlich sie ist, bleibt unklar; sie ist sogar sehr unterbrochen. Auch wie viel Integration tatsächlich stattfindet, ist nicht ganz offensichtlich.

Deshalb führe ich Ihnen jetzt Zitate aus dem extreme Programming an. Und wir werden beide Wörter getrennt analysieren.

Integration – Wie bereits erwähnt, streben wir danach, dass jeder Ingenieur mit der aktuellsten Version des Codes arbeitet und seinen Code so oft wie möglich in den Hauptbranch integriert, wobei wir kleine Branches bevorzugen. Denn wenn sie groß sind, können wir leicht eine Woche mit Merge-Konflikten stecken bleiben, insbesondere wenn wir einen langen Entwicklungszyklus wie Waterfall haben, wo ein Entwickler einen Monat an einer umfangreichen Funktion arbeitet. In der Integrationsphase könnte er dann sehr lange festhängen.

Integration bedeutet, dass wir unseren Branch nehmen und ihn mit dem Master zusammenführen, wir mergen ihn. Es gibt die ultimative Variante, wo wir als Transbase-Entwickler sofort in den Master schreiben, ohne unnötige Branches zu erzeugen.

Im Grunde genommen bedeutet Integration, seinen Code zu nehmen und ihn in den Master zu bringen.

Continuous Integration als Praxis, nicht Jenkins. Andrei Alexandrow

Was bedeutet hier das Wort „kontinuierlich“, was heißt Kontinuität? In der Praxis strebt der Entwickler danach, seinen Code so schnell wie möglich zu integrieren. Das ist sein Ziel bei jeder Aufgabe – dafür zu sorgen, dass sein Code so schnell wie möglich im Master verfügbar ist. In einer idealen Welt würden die Entwickler dies alle paar Stunden tun. D. h. du nimmst eine kleine Aufgabe, mergst sie in den Master. Alles ist großartig. Das ist dein Ziel. Und du musst es kontinuierlich tun. Sobald du etwas abgeschlossen hast, schiebst du es sofort in den Master.

Und der Entwickler, der etwas tut, ist dafür verantwortlich, dass es funktioniert und nichts kaputt macht. Genau hier kommt oft die Geschichte mit den Tests ins Spiel. Wir möchten einige Tests für unser Commit, für unser Merge durchführen, um sicherzustellen, dass alles funktioniert. Und hier kann Jenkins hilfreich sein.

Aber zu den Geschichten: Lassen Sie uns kleine Änderungen vornehmen, kleine Aufgaben stellen, und lassen Sie uns die Aufgabe gleich erledigen und versuchen, sie gleich in den Master-Branch zu integrieren – hier werden Ihnen keine Jenkins-Tools helfen. Denn Jenkins hilft Ihnen nur beim Ausführen von Tests.

Sie können auch ohne sie auskommen. Das wird Ihnen nicht im Geringsten schaden. Denn das Ziel der Praxis ist es, so oft wie möglich zu integrieren, um eine Menge Zeit für zukünftige Konflikte zu sparen.

Stellen wir uns vor, es ist das Jahr 2020 und aus irgendwelchen Gründen gibt es kein Internet. Und wir arbeiten lokal. Wir haben kein Jenkins. Das ist in Ordnung. Sie können immer noch einen lokalen Branch anlegen. Sie haben etwas Code geschrieben. Eine Aufgabe in 3-4 Stunden erledigt. Sie wechseln zu Master, führen git pull aus und integrieren Ihren Branch. Fertig. Wenn Sie das oft machen – herzlichen Glückwunsch, Sie haben Continuous Integration!

Continuous Integration als Praxis, nicht Jenkins. Andrei Alexandrow

Welche Beweise gibt es in der modernen Welt dafür, dass es sich lohnt, sich dafür zu engagieren? Denn insgesamt ist das kompliziert. Wenn Sie versuchen, so zu arbeiten, werden Sie feststellen, dass Sie einige Planungen anstoßen müssen. Sie müssen mehr Zeit für die Dekomposition von Aufgaben aufwenden. Denn wenn Sie das man... durchführen, können Sie nicht schnell zusammenführen und laufen entsprechend in Schwierigkeiten. Dann haben Sie keine Praxis mehr.

Und das wird teuer. Von morgen an mit Continuous Integration zu arbeiten, wird nicht funktionieren. Sie werden alle sehr lange brauchen, um sich daran zu gewöhnen, sehr lange, um die Dekomposition der Aufgaben zu lernen, und sehr lange, um die Review-Praxis zu ändern, falls Sie eine haben. Denn unser Ziel ist es, dass es heute zusammengeführt wird. Wenn Sie das Review über drei Tage hinweg durchführen, haben Sie Probleme, und Continuous Integration funktioniert nicht.

Aber gibt es aktuell gültige Beweise, die uns sagen, dass es sinnvoll ist, in diese Praxis zu investieren?

Continuous Integration als Praxis, nicht Jenkins. Andrei Alexandrow

Das Erste, was mir in den Sinn kam, ist der State of DevOps. Diese Studie wird seit sieben Jahren durchgeführt. Momentan geschieht das als unabhängige Organisation, aber unter Google.

Eine Studie aus dem Jahr 2018 hat gezeigt, dass Unternehmen, die auf kurzlebige Branches setzen – die sich schnell und häufig integrieren – deutlich bessere IT-Leistungskennzahlen aufweisen.

Welche Kennzahlen sind das? Es sind vier Metriken, die in ihren Umfragen von allen Unternehmen erfasst werden: Häufigkeit der Bereitstellungen, Vorlaufzeit für Änderungen, Wiederherstellungszeit des Dienstes und Änderungsfehlerquote.

Erstens gibt es diese Korrelation: Wir wissen, dass Unternehmen, die häufig mergen, in diesen Metriken erheblich besser abschneiden. Es gibt eine Klassifizierung der Unternehmen in verschiedene Kategorien: langsame Unternehmen, die Dinge langsam produzieren, mittelmäßige Leistungsträger, hochleistende Unternehmen und die Elite. Die Elite umfasst Unternehmen wie Netflix und Amazon, die extrem schnell, effizient und qualitativ hochwertig arbeiten.

Continuous Integration als Praxis, nicht Jenkins. Andrei Alexandrow

Eine weitere Geschichte, die vor nur einem Monat passiert ist: Im Technology Radar erschien ein interessanter Artikel über Gitflow. Gitflow unterscheidet sich von anderen Methoden dadurch, dass seine Branches lange leben. Es gibt Release-Branches, die lange bestehen, sowie Feature-Branches, die ebenfalls lange aktiv bleiben. Diese Praxis wurde im Technology Radar als "HOLD" eingestuft. Warum? Weil die Menschen bei der Integration auf Probleme stoßen.

Wenn ein Branch sehr lange besteht, stockt er, verfault und wir investieren zunehmend mehr Zeit, um eine Änderung darin vorzunehmen.

Vor kurzem äußerte der Autor von Gitflow, dass, wenn Sie auf Continuous Integration abzielen und so oft wie möglich Deployments durchführen möchten, Gitflow eine schlechte Idee ist. In einem zusätzlichen Abschnitt des Artikels stellte er klar, dass Gitflow überflüssig ist, wenn Sie eine Backend-Umgebung haben, die dies unterstützt, weil Gitflow Sie verlangsamen und Integrationsprobleme verursachen kann.

Das bedeutet nicht, dass Gitflow schlecht ist und nicht verwendet werden sollte. Es ist für andere Fälle geeignet. Zum Beispiel, wenn Sie mehrere Versionen eines Dienstes oder einer Anwendung unterstützen müssen, also wenn Sie über einen längeren Zeitraum Support leisten müssen.

Aber wenn Sie mit Leuten sprechen, die solche Dienste betreuen, hören Sie oft von den Schwierigkeiten, dass die Version 3.2, die vor 4 Monaten veröffentlicht wurde, diesen Fix nicht enthält, und um ihn einzufügen, müssen viele Änderungen vorgenommen werden. Und dann stecken sie wieder fest und kämpfen eine Woche lang, um eine neue Funktion zusammenzuführen.

Wie Alexander Kovalev im Chat richtig bemerkte, bedeutet Korrelation nicht, dass es eine Kausalität gibt. Das ist richtig. Das heißt, es gibt keine direkte Verbindung, dass wenn Sie Continuous Integration haben, alle Metriken großartig sein werden. Aber es besteht eine positive Korrelation: wenn das eine vorhanden ist, ist es wahrscheinlich, dass auch das andere zutrifft. Es ist kein Fakt, aber wahrscheinlich. Es ist lediglich eine Korrelation.

Continuous Integration als Praxis, nicht Jenkins. Andrei Alexandrow

Es scheint, als würden wir bereits etwas tun, als würden wir bereits mergen. Aber wie können wir sicherstellen, dass wir tatsächlich Continuous Integration haben und oft genug mergen?

Jez Humble ist der Autor des Handbuchs, 'Accelerate', der Website für Continuous Delivery und des Buches 'Continuous Delivery'. Er schlägt diesen Test vor:

  • Der Code des Ingenieurs wird täglich in den Master integriert.
  • Bei jedem Commit führen Sie Unit-Tests aus.
  • Der Build im Master ist gescheitert, wurde aber innerhalb von etwa 10 Minuten behoben.

Er schlägt vor, einen solchen Test zu verwenden, um sicherzustellen, dass diese Praxis bei Ihnen tatsächlich vorhanden ist.

Ich finde das letzte ein wenig umstritten. Das heißt, wenn Sie es innerhalb von 10 Minuten reparieren können, haben Sie Continuous Integration. Das klingt für mich ein wenig merkwürdig, aber es macht Sinn. Warum? Weil häufige Merges bedeuten, dass Ihre Änderungen klein sind. Wenn eine kleine Änderung dazu führt, dass Ihr Master-Build fehlschlägt, können Sie schnell einen konkreten Fehler finden, weil die Änderungen minimal sind. Nehmen wir an, Sie hatten einen kleinen Merge, der 20-30 Zeilen geändert hat. Dadurch können Sie schnell nachvollziehen, was das Problem war, da die Änderungen sehr gering sind, und Sie haben ein sehr kleines Suchgebiet für das Problem.

Selbst wenn unser Produzenten-System nach einem Release zusammenbricht, wird es uns mit einer Continuous Integration-Praxis viel leichter fallen, zu reagieren, da die Änderungen klein sind. Ja, das wird die Planung beeinflussen. Es wird schmerzhaft sein. Und wahrscheinlich ist das Schwierigste an dieser Praxis, sich daran zu gewöhnen, die Aufgaben zu unterteilen. Das heißt, herauszufinden, wie man etwas in ein paar Stunden erledigt und dabei ein Review durchläuft, falls man eines hat. Das Review ist ein ganz anderes Problem.

Unit-Tests sind einfach Werkzeuge, die Ihnen helfen zu verstehen, ob Ihre Integration erfolgreich war und ob alles funktioniert. Meiner Meinung nach ist dies auch kein zwingender Punkt, denn der Sinn der Praxis liegt nicht darin.

Das ist eine kurze Zusammenfassung über Continuous Integration. Das ist alles, was es zu dieser Praxis zu sagen gibt. Ich bin bereit für Fragen.

Ich fasse nur kurz zusammen:

  • Continuous Integration ist nicht Jenkins, es ist nicht GitLab.
  • Es ist kein Tool, sondern eine Praxis, bei der wir unseren Code so oft wie möglich in den Master-Branch mergen.
  • Wir tun dies, um die große Schmerzen zu vermeiden, die durch spätere Merges entstehen. Das bedeutet, wir erleben jetzt kleine Schmerzen, um in der Zukunft nicht große Schmerzen zu erleiden. Das ist der gesamte Sinn.
  • Außerhalb findet Kommunikation über den Code statt, aber ich sehe das sehr selten. Dennoch ist das auch ein Teil des Konzepts.

Fragen

Was ist zu tun, wenn Aufgaben nicht dekonstruiert werden können?

Dekonstruieren. Was ist das Problem? Können Sie ein Beispiel nennen, bei dem eine Aufgabe nicht dekonstruiert werden kann?

Es gibt Aufgaben, die lassen sich überhaupt nicht von Grund auf zerlegen, wie beispielsweise solche, die eine sehr tiefgehende Expertise erfordern und deren Lösung tatsächlich Monate in Anspruch nehmen kann, um zu einem akzeptablen Ergebnis zu gelangen.

Wenn ich dich richtig verstanden habe, gibt es also eine große und komplexe Aufgabe, deren Ergebnis erst in einem Monat sichtbar sein wird?

Ja, genau. Das Ergebnis wird frühestens in einem Monat abschätzbar sein.

Gut. Im Großen und Ganzen ist das kein Problem. Warum? Weil wir in diesem Fall, wenn wir über Branches sprechen, nicht von einer Branch mit einer Funktion sprechen. Funktionen können umfangreich und komplex sein und viele Komponenten betreffen. Möglicherweise können wir sie nicht in einer einzigen Branch vollständig umsetzen. Das ist in Ordnung. Wir müssen lediglich diese Aufgabe aufteilen. Wenn eine Funktion noch nicht vollständig fertig ist, bedeutet das nicht, dass Teile ihres Codes nicht gemergt werden können. Angenommen, du hast eine Migration durchgeführt, und innerhalb der Funktion gibt es verschiedene Schritte. Zum Beispiel hast du den Schritt – Migration erstellen, eine neue Methode hinzufügen. Diese Dinge kannst du bereits täglich mergen.

Gut. Was ist also der Sinn dahinter?

Was bringt es, täglich kleine Änderungen zusammenzuführen?

Ja.

Wenn etwas bei dir kaputtgeht, siehst du das sofort. Mit einem kleinen Stück ist es einfacher, den Fehler zu beheben. Der Sinn ist, dass es viel einfacher ist, jetzt kleine Änderungen zusammenzuführen, als in ein paar Wochen etwas Größeres zu mergen. Und der dritte Punkt ist, dass andere Ingenieure mit der bereits aktuellen Version des Codes arbeiten. Sie werden sehen, dass hier einige Migrationen hinzugefügt wurden und hier eine Methode erschienen ist, die sie möglicherweise ebenfalls verwenden möchten. Alle werden sehen, was in deinem Code passiert. Genau aus diesen drei Gründen wird diese Praxis etabliert.

Danke, die Frage ist damit geklärt!

(Oleg Soroka) Darf ich etwas hinzufügen? Du hast alles richtig gesagt, ich möchte nur einen Satz hinzufügen.

So.

Bei Continuous Integration wird der Code nicht dann in den Hauptbranch zusammengeführt, wenn die Funktion vollständig entwickelt ist, sondern sobald der Build nicht mehr fehlerhaft ist. Dadurch können Sie unbesorgt so oft wie gewünscht am Tag ins Master committen. Ein weiterer Aspekt ist, dass, wenn es Ihnen aus irgendeinem Grund nicht möglich ist, eine monatliche Aufgabe in kleinere Aufgaben von mindestens drei Tagen zu gliedern – ich schweige von drei Stunden –, dann haben Sie ein großes Problem. Und die Tatsache, dass Sie kein Continuous Integration haben, ist das kleinere Problem. Das bedeutet, dass es Probleme mit der Architektur und den ingenieurtechnischen Praktiken gibt, die bei Null stehen. Denn selbst wenn es sich um Forschung handelt, muss sie in jedem Fall als Hypothesen oder Zyklen formuliert werden.

Wir haben von vier Kennzahlen gesprochen, die erfolgreiche Unternehmen von weniger erfolgreichen unterscheiden. Zunächst müssen wir jedoch die Voraussetzungen schaffen, um zu diesen vier Kennzahlen zu gelangen. Wenn es im Durchschnitt einen Monat dauert, eine Aufgabe zu erledigen, würde ich mich zunächst auf diese Kennzahl konzentrieren und sie auf drei Tage reduzieren. Danach könnten wir über Continuous Integration nachdenken.

Habe ich dich richtig verstanden, dass du denkst, dass es keinen Sinn macht, in ingenieurtechnische Praktiken zu investieren, wenn jede Aufgabe einen Monat dauert?

Sie verfügen über Continuous Integration. Dabei gibt es die Möglichkeit, innerhalb von 10 Minuten entweder eine Korrektur vorzunehmen oder zurückzusetzen. Stellen Sie sich vor, Sie haben es veröffentlicht. Und selbst wenn Sie Continuous Deployment verwenden und es auf die Produktionsumgebung ausgerollt haben, bemerken Sie erst danach, dass etwas schiefgelaufen ist. Nun müssen Sie es zurücksetzen, aber Ihre Datenbankmigration hat bereits stattgefunden. Ihr Datenbankschema ist auf die nächste Version aktualisiert, außerdem gab es ein Backup, und es wurden auch Daten hinzugefügt.

Was ist Ihre Alternative? Wenn Sie den Code zurücksetzen, kann dieser nicht mehr mit der aktualisierten Datenbank arbeiten.

Die Entwicklung erfolgt nur in eine Richtung, richtig?

Menschen mit schwachen Ingenieurpraktiken haben wahrscheinlich auch kein dickes Buch über ... gelesen. Was tun mit Backups? Wenn Sie aus einem Backup wiederherstellen, verlieren Sie die Daten, die in diesem Zeitraum gesammelt wurden. Zum Beispiel haben Sie drei Stunden mit der neuen Version der Datenbank gearbeitet, und Benutzer haben sich registriert. Sie kehren auf ein altes Backup zurück, weil das Schema mit der neuen Version nicht funktioniert, und somit haben Sie diese Benutzer verloren. Und sie sind unzufrieden und beschweren sich.

Um ein umfassendes Verständnis für die Praktiken zu erlangen, die Continuous Integration und Continuous Delivery unterstützen, reicht es nicht aus, einfach nur zu lernen, wie man schreibt. Erstens kann es sehr viele verschiedene Praktiken geben, was unpraktisch wäre. Zudem gibt es viele andere Ansätze, wie die wissenschaftliche Methode. Diese wurde zum Beispiel einmal von GitHub populär gemacht. Dabei läuft sowohl der alte Code als auch der neue Code gleichzeitig. Es handelt sich um die Entwicklung einer unvollendeten Funktion, die jedoch ein Ergebnis zurückgeben kann, sei es als Funktion oder als REST API. Du führst sowohl den neuen als auch den alten Code aus und vergleichst die Unterschiede zwischen ihnen. Wenn es eine Abweichung gibt, protokollierst du dieses Ereignis. Auf diese Weise weißt du, dass deine neue Funktion bereit ist, über den alten Code ausgerollt zu werden, wenn es über einen bestimmten Zeitraum keine Unterschiede zwischen diesen beiden gibt.

Solcher Praktiken gibt es Hunderte. Ich würde empfehlen, mit der Transbase-Entwicklung zu beginnen. Sie basiert nicht vollständig auf Continuous Integration, aber die Praktiken sind die gleichen; ein Aspekt lebt schlecht ohne den anderen.

Hast du die Transbase-Entwicklung als Beispiel gegeben, um zu zeigen, wo man Praktiken betrachten kann, oder empfiehlst du den Leuten, mit der Transbase-Entwicklung zu beginnen?

Schau dir das an, denn sie können es nicht nutzen. Um sie zu verwenden, muss man viel lesen. Und wenn die Frage bei jemandem auftaucht: "Was soll ich mit einer Funktion anfangen, die einen Monat dauert?", bedeutet das, dass er nicht über die Entwicklung von Transbase gelesen hat. Ich würde es nicht empfehlen. Ich würde vorschlagen, sich ausschließlich auf das Thema zu konzentrieren, wie man große Aufgaben architektonisch in kleinere aufteilen kann. Das ist das Wesen der Dekomposition.

Dekomposition ist eines der Werkzeuge des Architekten. Zuerst führen wir eine Analyse durch, dann die Dekomposition, gefolgt vom Synthese und schließlich der Integration. So fügt sich alles zusammen. Und wir müssen zu Continuous Integration durch Dekomposition hindurch wachsen. Fragen tauchen in der ersten Phase auf, und wir sprechen bereits über die vierte Phase, d. h. je häufiger man die Integration durchführt, desto besser. Es wäre noch zu früh, sie zu machen; es wäre besser, zunächst an seinem Monolithen zu arbeiten.

Es müssen einige Pfeile und Kästchen auf einem Diagramm gezeichnet werden. Man kann nicht einfach sagen, ich zeige jetzt das Architekturdiagramm einer neuen Anwendung und präsentiere ein Kästchen, in dem sich ein grüner Knopf für die Anwendung befindet. In jedem Fall wird es mehr Kästchen und Pfeile geben. In jedem Diagramm, das ich gesehen habe, waren es mehr als eins. Und die Dekomposition erfolgt sogar auf der Ebene der grafischen Darstellung. Daher können die Kästchen unabhängig gemacht werden. Wenn nicht, habe ich große Fragen an den Architekten.

Es gibt eine Frage aus dem Chat: „Wenn das Review obligatorisch ist und lange dauert, also einen Tag oder mehr?“

Sie haben Probleme mit dem Review-Prozess. Ein Review sollte nicht länger als einen Tag dauern. Das steht im Zusammenhang mit der vorherigen Frage, nur etwas entschärft. Wenn ein Review einen Tag dauert, dann handelt es sich wahrscheinlich um eine sehr umfangreiche Änderung, und es sollte kleiner gehalten werden. Im Transbase-Development, das Oleg empfohlen hat, gibt es einen Ansatz namens Continuous Review. Die Idee dahinter ist, dass wir absichtlich sehr kleine Pull Requests erstellen, weil wir darauf abzielen, kontinuierlich und schrittweise zu mergen. Daher ändert der Pull Request nur eine Abstraktion oder 10 Zeilen. Dadurch benötigt das Review nur ein paar Minuten.

Wenn das Review einen Tag oder länger dauert, stimmt etwas nicht. Zunächst einmal könnte es an Ihrer Architektur liegen. Vielleicht handelt es sich um einen großen Codeabschnitt von beispielsweise 1.000 Zeilen. Oder Ihre Architektur ist so komplex, dass sie für andere schwer verständlich ist. Das ist ein seitliches Problem, das auch gelöst werden muss. Möglicherweise ist ein Review überhaupt nicht notwendig. Das sollten Sie ebenfalls in Betracht ziehen. Ein Review kann ein Hemmschuh sein. Es bringt zwar einige Vorteile, aber Sie müssen verstehen, warum Sie es durchführen. Ist es ein schneller Weg, Informationen zu übermitteln, oder möchten Sie interne Standards festlegen? Wozu brauchen Sie das? Denn ein Review sollte entweder sehr schnell erfolgen oder gänzlich entfällt. Es geht darum wie bei der Transbase-Entwicklung – eine sehr schöne Geschichte, aber nur für erfahrene Leute.

Was die vier Metriken angeht, würde ich empfehlen, sie dennoch zu erfassen, um zu verstehen, wohin das führt. Schauen Sie sich die Zahlen an, sehen Sie sich das Bild an, um zu erkennen, wie schlecht die Situation ist.

(Dmitri) Ich bin bereit, mit dir darüber zu diskutieren. Zahlen und Metriken sind großartig, praktikable Lösungen auch. Aber man muss verstehen, ob das für das Geschäft notwendig ist. Es gibt Unternehmen, die nicht so schnell Änderungen umsetzen müssen. Ich kenne Firmen, bei denen man nicht alle 15 Minuten Änderungen vornehmen kann. Und das liegt nicht daran, dass sie schlecht sind. Das ist einfach der Lebenszyklus. Um Feature-Branches und Feature-Toggles zu implementieren, sind tiefgehende Kenntnisse notwendig.

Das ist kompliziert. Wenn Sie mehr über die Geschichte von Feature-Toggles lesen möchten, kann ich Ihnen das sehr empfehlen. https://trunkbaseddevelopment.com/. Und es gibt einen großartigen Artikel von Martin Fowler über Feature-Toggles: welche Typen es gibt, Lebenszyklen usw. Feature-Toggles sind komplex.

Und du hast trotzdem die Frage nicht beantwortet: „Ist Jenkins notwendig oder nicht?“

Jenkins ist in keiner Hinsicht notwendig. Ernsthaft, Werkzeuge wie Jenkins und Gitlab bieten Ihnen Komfort. Sie werden sehen, ob der Build erfolgreich war oder nicht. Sie können Ihnen helfen, aber sie bieten Ihnen keine Praxis. Sie können Ihnen nur ein „Ok“ oder „Nicht Ok“ geben. Und das, falls Sie überhaupt Tests schreiben, denn wenn es keine Tests gibt, ist es fast sinnlos. Daher ist es zwar bequemer, aber insgesamt kann man auch ohne ihn leben und verliert nicht viel.

Das heißt, wenn Sie Praktiken haben, benötigen Sie ihn nicht?

Ganz genau. Ich empfehle den Test von Jez Humble. Zuletzt habe ich eine gespaltene Meinung zu dem letzten Punkt. Aber insgesamt, wenn Sie drei Dinge haben: Sie führen ständig Merges durch, Sie führen Tests bei Commits im Master aus und beheben Builds im Master schnell, dann benötigen Sie wahrscheinlich nicht viel mehr.

Während wir auf Fragen der Teilnehmer warten, habe ich eine Frage. Wir haben gerade über Produktcode gesprochen. Hast du auch Infrastrukturcode verwendet? Ist das derselbe Code, mit denselben Prinzipien und demselben Lebenszyklus, oder gibt es dort andere Lebenszyklen und Prinzipien? Normalerweise, wenn alle über Continuous Integration und Development sprechen, wird oft vergessen, dass es auch Infrastrukturcode gibt. In letzter Zeit wird das immer mehr. Sollten diese Regeln auch dort Anwendung finden?

Es wäre nicht nur angebracht, es wäre großartig, denn es würde das Leben definitiv erleichtern. Sobald wir mit Code arbeiten, nicht nur mit Bash-Skripten, sondern mit richtigem Code.

Halt, halt, ein Bash-Skript ist auch Code. Fass meine alte Liebe nicht an.

Gut, ich werde deine Erinnerungen nicht verletzen. Ich habe eine persönliche Abneigung gegen Bash. Es bricht oft unschön und unvorhersehbar, deshalb mag ich es nicht. Aber nehmen wir an, du hast Code in Bash. Vielleicht habe ich wirklich keine Ahnung, und dort gibt es gute Testframeworks. Ich bin einfach nicht auf dem Laufenden. Und wir erhalten die gleichen Vorteile.

Sobald wir mit Infrastruktur als Code arbeiten, stoßen wir auf die gleichen Probleme wie Entwickler. Vor einigen Monaten erhielt ich einen Pull-Request mit 1.000 Zeilen Bash-Code von einem Kollegen. Und man bleibt vier Stunden mit dem Review beschäftigt. Die Probleme sind die gleichen. Es ist immer noch Code. Und immer noch Teamarbeit. Wir stecken bei dem Pull-Request fest und haben die gleichen Merge-Konflikte beim Bash zu lösen.

Ich beschäftige mich derzeit intensiv mit der attraktiven Programmierung von Infrastrukturen. Ich habe Pulumi in die Infrastruktur integriert. Das ist reines Programmieren. Dort sieht es noch ansprechender aus, weil ich alle Möglichkeiten der Programmiersprache habe, d. h. ich habe mit den gleichen If-Bedingungen an einer Stelle schöne Toggle erstellt, und alles läuft hervorragend. Mein Beitrag ist bereits im Master. Alle können ihn sehen. Die anderen Ingenieure sind darüber informiert. Es hat bereits Auswirkungen gehabt. Allerdings wurde es nicht für alle Infrastrukturen aktiviert, sondern zunächst für meine Testumgebungen. Um deine Frage noch einmal zu beantworten: Ja, es ist notwendig. Es erleichtert uns Ingenieuren, die mit Code arbeiten, definitiv das Leben.

Gibt es noch weitere Fragen?

Ich habe eine Frage. Ich möchte die Diskussion mit Oleg fortsetzen. Insgesamt denke ich, dass Sie recht haben, dass wenn eine Aufgabe einen Monat in Anspruch nimmt, es ein Problem mit der Architektur gibt, es ein Problem mit der Analyse, Dekomposition, Planung usw. gibt. Aber ich habe das Gefühl, dass wenn Sie beginnen, nach Continuous Integration zu leben, Sie anfangen werden, die Probleme mit der Planung zu beheben, denn das wird Ihnen nicht erspart bleiben.

(Oleg) Ja, das stimmt. In Bezug auf den Aufwand ist diese Praxis mit jeder anderen ernsthaften Praxis vergleichbar, die die Kultur verändert. Das Schwierigste beim Überwinden sind die Gewohnheiten, insbesondere die schlechten Gewohnheiten. Und wenn es notwendig ist, diese Praxis umzusetzen, erfordert das eine ernsthafte Änderung der Gewohnheiten der Beteiligten: Entwickler, Management, Produktionsleiter, dann können Sie mit Überraschungen rechnen.

Welche Überraschungen könnten auftreten? Angenommen, Sie haben beschlossen, häufiger Integrationen durchzuführen. Und an der Integration sind einige andere Dinge gebunden, zum Beispiel Artefakte. In Ihrem Unternehmen gibt es möglicherweise die Regel, dass jedes Artefakt in irgendeinem System zur Lagerung von Artefakten erfasst werden muss. Und das nimmt Zeit in Anspruch. Eine Person muss bestätigen, dass sie als Release-Manager dieses Artefakt auf die Produktionsbereitschaft getestet hat. Wenn das 5-10-15 Minuten dauert und Sie einmal pro Woche ein Release machen, dann ist es nicht viel, wenn man einmal pro Woche eine halbe Stunde aufwendet.

Wenn Sie Continuous Integration 10 Mal am Tag durchführen, dann müssen Sie 10 Mal 30 Minuten berechnen. Und das übersteigt die Arbeitszeit dieses Release-Managers. Er wird müde, das zu tun. Es gibt ständig laufende Kosten für bestimmte Praktiken. Und das ist alles.

Sie müssen entweder diese Regel aufheben, sodass Sie sich nicht mehr mit so etwas beschäftigen, d.h. Sie ordnen nicht mehr manuell einen Grad der Übereinstimmung von etwas zu etwas anderem zu. Sie verlassen sich vollständig auf einen automatisierten Satz von Tests zur Bereitstellung.

Und wenn Sie von jemandem eine Bestätigung benötigen, damit der Hauptverantwortliche unterschreibt, und Sie ohne Vasja, der sagt, dass er es erlaubt, nicht in die Produktion gehen, dann stören all diese Dinge die Praktiken. Denn wenn es mit Steuern verbundene Aktivitäten gibt, vervielfacht sich alles um das 100-Fache. Deshalb wird eine Veränderung oft nicht von allen mit Freude wahrgenommen. Denn es ist schwierig, die Gewohnheiten der Menschen zu ändern.

Wenn jemand eine gewohnte Arbeit verrichtet, macht er das, ohne groß darüber nachzudenken. Die kognitive Belastung ist gleich null. Er arbeitet einfach nach einem vorgefertigten Plan, in seinem Kopf gibt es bereits eine Checkliste, er hat es tausendmal gemacht. Und sobald Sie kommen und ihm sagen: 'Lass uns diese Praxis abschaffen und ab Montag eine neue einführen', wird das für ihn eine erhebliche kognitive Belastung. Und diese tritt sofort für alle ein.

Es ist zwar nicht für jeden möglich, sich diesen Luxus zu leisten, aber ich handhabe es immer so: Wenn ein neues Projekt startet, werden gewöhnlich alle ungeprüften Praktiken sofort integriert. Solange das Projekt noch in den Anfängen steckt, sind wir praktisch nicht gefährdet. Es gibt noch kein Prod, also gibt es nichts, was wir ruinieren könnten. Daher können wir diesen Ansatz als Übung nutzen. Das funktioniert. Allerdings haben nicht alle Unternehmen die Möglichkeit, solche Projekte häufig zu starten. Das ist auch etwas seltsam, da es derzeit einen umfassenden digitalen Wandel gibt; alle sollten Experimente durchführen, um mit der Konkurrenz Schritt zu halten.

Hier kommen wir zu dem Punkt, dass man zunächst ein klares Verständnis dafür haben muss, was man tun möchte. Die Welt ist nicht perfekt, und das Prod ist ebenfalls nicht ideal.

Ja, diese Dinge sind miteinander verbunden.

Auch Unternehmen haben oft nicht den klaren Überblick, dass sie genau in diese Richtung gehen sollten.

Es gibt Situationen, in denen wirklich keine Änderungen möglich sind. Das ist der Fall, wenn der Druck auf das Team steigt. Das Team ist bereits ziemlich erschöpft. Es hat keine Zeit für Experimente. Von morgens bis abends entwickeln sie neue Funktionen. Und das Management verlangt immer mehr. In einer solchen Situation sind Veränderungen unmöglich. Das Team kann nur gesagt werden, dass es morgen genau wie gestern arbeiten wird, nur dass die Funktionen etwas umfangreicher sein müssen. Es sind keine Übergänge zu anderen Praktiken möglich. Dies ist die klassische Situation, in der es keine Zeit zum Feilen gibt, weil die Bäume gefällt werden müssen; also wird mit einer stumpfen Axt gearbeitet. Hier gibt es keine einfachen Ratschläge.

(Dmitrij) Ich lese eine Klarstellung aus dem Chat vor: „Aber es ist wichtig, eine umfassende Testabdeckung auf verschiedenen Ebenen zu haben. Wie viel Zeit wird für Tests aufgewendet? Das ist irgendwie teuer und nimmt viel Zeit in Anspruch.“

(Oleg) Das ist ein klassisches Missverständnis. Es sollten genügend Tests vorhanden sein, damit Sie selbst sicher sind. Continuous Integration ist nicht so aufgebaut, dass zuerst 100 % der Tests durchgeführt werden, und erst dann fangen Sie an, diese Praxis anzuwenden. Continuous Integration reduziert Ihre kognitive Belastung, da jede Änderung, die Sie sehen, so offensichtlich ist, dass Sie verstehen, ob sie etwas kaputt macht oder nicht, auch ohne Tests. Sie können dies mental schnell testen, weil es sich um kleine Änderungen handelt. Selbst wenn Sie nur manuelle Tester haben, wird es auch einfacher für sie. Sie haben das Update herausgebracht und gesagt: „Schau mal, ist etwas kaputt?“ Sie haben überprüft und gesagt: „Nein, es ist nichts kaputt.“ Weil der Tester weiß, wo er hinschauen muss. Ihr Commit ist mit einem bestimmten Codeabschnitt verknüpft. Und das wird durch ein spezifisches Verhalten ausgelöst.

Das hast du natürlich geschönt.

(Dmitry) Da stimme ich nicht zu. Es gibt die Praxis des Testgetriebenen Entwickelns, die genau davor schützt.

(Oleg) Ich habe das bisher noch nicht angesprochen. Die erste Illusion ist, dass man 100 % Tests schreiben muss oder dass man sich überhaupt nicht mit Continuous Integration befassen sollte. Das ist nicht wahr. Es sind zwei parallele Praktiken, die nicht direkt voneinander abhängen. Ihre Testabdeckung sollte optimal sein. Optimal bedeutet, dass Sie selbst sicher sind, dass die Qualität des Masters, die nach einem Commit übrig bleibt, Ihnen erlaubt, am Freitagabend betrunken den „Deploy“-Button mit Zuversicht zu drücken. Wie erreichen Sie das? Durch Reviews, durch Abdeckung, durch gutes Monitoring.

Gutes Monitoring ist nicht zu unterscheiden von Tests. Wenn Sie Tests einmal in der Pre-Prod-Phase ausführen, überprüfen sie alle Ihre Anwendungsfälle nur einmal. Wenn Sie jedoch einen unendlichen Zyklus laufen lassen, ist dies Ihr ausgebautes Monitoring-System, das ständig alles testet – ob es ausgefallen ist oder nicht. In diesem Fall besteht der Unterschied nur in der Einmaligkeit oder der Wiederholung. Ein sehr gutes Set an Tests …, die unendlich oft ausgeführt werden, ist Monitoring. Und richtiges Monitoring sollte genau so sein.

Und wie genau erreichen Sie den Zustand, in dem Sie sich am Freitagabend bereit fühlen, nach Hause zu gehen, ist eine ganz andere Frage. Vielleicht sind Sie einfach ein mutiger Draufgänger.

Lassen Sie uns ein wenig zu Continuous Integration zurückkehren. Wir sind ein wenig in eine andere komplexe Praxis abgeschweift.

Und die zweite Illusion ist, dass MVP schnell erstellt werden sollte, weshalb Tests überhaupt nicht notwendig sind. Das ist nicht ganz richtig. Wenn Sie bei einem MVP eine User Story schreiben, können Sie entweder impulsiv handeln, indem Sie gehört haben, dass es eine bestimmte User Story gibt, und sofort mit dem Codieren beginnen, oder Sie arbeiten nach TDD. Die Erfahrung zeigt, dass es nicht länger dauert, wenn Sie nach TDD arbeiten; Tests sind ein Nebeneffekt. Die TDD-Praxis besteht nicht darin, zu testen. Obwohl es Test Driven Development heißt, geht es dabei eigentlich nicht um Tests. Es ist eher ein architektonischer Ansatz. Es geht darum, genau das zu schreiben, was benötigt wird, und das, was nicht benötigt wird, zu vermeiden. Diese Praxis konzentriert sich auf die nächste Iteration Ihrer Gedanken hinsichtlich des Aufbaus der Anwendungsarchitektur.

Es ist also nicht einfach, sich von diesen Illusionen zu befreien. MVP und Tests stehen nicht im Widerspruch zueinander. Ganz im Gegenteil, wenn Sie Ihr MVP gemäß der TDD-Praxis erstellen, werden Sie es besser und schneller umsetzen, als wenn Sie dies ohne Praktiken und zufällig tun.

Das ist eine sehr nicht offensichtliche und komplexe Überlegung. Wenn man hört, dass ich jetzt noch Tests schreiben werde und gleichzeitig schneller etwas machen kann, klingt das absolut unplausibel.

(Dmitrij) Viele, wenn sie von MVP sprechen, haben einfach keine Lust, etwas Vernünftiges zu schreiben. Und das sind letztendlich verschiedene Dinge. Man sollte MVP nicht in etwas Schlechtes verwandeln, das nicht funktioniert.

Ja, ja, du hast recht.

Und dann plötzlich das MVP in der Produktion.

Für immer.

TDD klingt zunächst ungewöhnlich, wenn man hört, dass man Tests schreibt und scheinbar mehr Arbeit leistet. Es mag merkwürdig erscheinen, aber tatsächlich läuft es so schneller und ansprechender. Wenn du einen Test schreibst, denkst du bereits intensiv darüber nach, welcher Code geschrieben wird, wie er aufgerufen wird und welches Verhalten wir davon erwarten. Du sagst nicht einfach, dass du eine Funktion geschrieben hast, die etwas tut. Stattdessen überlegst du dir zuerst, welche Bedingungen sie erfüllen muss und wie sie aufgerufen werden soll. Du deckst das mit Tests ab und daraus erkennst du, wie die Schnittstellen innerhalb deines Codes aussehen werden. Das hat einen erheblichen Einfluss auf die Architektur. Dein Code wird automatisch modularer, weil du zuerst versuchst zu verstehen, wie du ihn testen wirst, bevor du ihn schreibst.

Ich hatte mit TDD die Erfahrung, dass ich zu einem bestimmten Zeitpunkt einen Mentor für Ruby engagiert habe, als ich noch Ruby-Programmierer war. Und er sagt: "Lass uns TDD machen." Ich dachte mir: "Mist, jetzt muss ich noch etwas zusätzlich schreiben." Und wir haben vereinbart, dass ich in den nächsten zwei Wochen den gesamten funktionierenden Code in Python nach TDD schreibe. Nach zwei Wochen wurde mir klar, dass ich nicht mehr zurück will. Wenn man zwei Wochen lang versucht, dies überall anzuwenden, merkt man, wie viel einfacher es wird, auch nur zu denken. Aber das ist nicht offensichtlich, deshalb empfehle ich jedem, wenn ihr das Gefühl habt, dass TDD kompliziert, langwierig und überflüssig ist, haltet euch zwei Wochen lang daran. Mir haben zwei Wochen dafür gereicht.

(Dmitrij) Wir können diesen Gedanken aus der Perspektive des Infrastrukturmanagements weiterentwickeln. Bevor wir etwas Neues einführen, führen wir ein Monitoring durch und starten dann. In diesem Fall wird das Monitoring zu einem regulären Test. Es gibt eine Entwicklung durch Monitoring. Aber fast alle sagen, dass es lange dauert, dass sie es als lästig empfinden und einen vorläufigen Entwurf erstellt haben. Wenn wir das Monitoring richtig durchführen, verstehen wir den Zustand des CI-Systems. In einem CI-System gibt es viel Monitoring. Wir verstehen den Zustand des Systems und wissen, was darin vor sich geht. Und während der Entwicklung gestalten wir das System so, dass es den gewünschten Zustand erreicht.

Diese Praktiken sind seit langem bekannt. Wir haben das vor etwa 4 Jahren besprochen. Aber in den letzten 4 Jahren hat sich praktisch nichts geändert.

Ich schlage vor, die offizielle Diskussion an dieser Stelle zu beenden.

Video (als Medienelement eingefügt, funktioniert aber aus irgendeinem Grund nicht):

https://youtu.be/zZ3qXVN3Oic

Quelle: habr.com

Kaufen Sie zuverlässiges Hosting für Websites mit DDoS-Schutz, VPS VDS-Server 🔥 Kaufen Sie zuverlässiges Hosting für Websites mit DDoS-Schutz, VPS VDS-Server | ProHoster