Vor einem Jahr habe ich angefangen, Vollzeit bei Bloomberg zu arbeiten. Und dann habe ich beschlossen, diesen Artikel zu schreiben. Ich dachte, ich wäre voller Ideen, die ich zu gegebener Zeit auf Papier werfen könnte. Aber innerhalb eines Monats wurde mir klar, dass nicht alles so einfach sein wird: Ich habe bereits begonnen zu vergessen, was ich gelernt habe. Entweder war das Wissen so gut erworben, dass mein Verstand mich glauben ließ, dass ich es immer wusste, oder sie flogen einfach aus meinem Kopf.
1Dies ist einer der Gründe, warum ich angefangen habe, ein Tagebuch zu führen. Jeden Tag, als ich in interessante Situationen geriet, beschrieb ich sie. Und das alles dank der Tatsache, dass ich neben einem führenden Programmierer saß. Ich konnte seine Arbeit genau beobachten und sah, wie unterschiedlich sie von dem war, was ich tun würde. Wir haben viel zusammen programmiert, was meine Beobachtungen noch einfacher machte. Darüber hinaus verurteilt unser Team nicht das „Ausspionieren“ von Personen, die Code schreiben. Als mir etwas Interessantes passierte, drehte ich mich um und schaute. Dank des ständigen Aufstiegs war ich mir immer bewusst, was geschah.
Ich verbrachte ein Jahr neben einem führenden Programmierer. Das habe ich gelernt.
Inhalt
Code schreiben
Wie man Dinge im Code benennt
Eine meiner ersten Aufgaben war die Arbeit an React UI. Wir hatten eine Hauptkomponente, die alle anderen Komponenten enthielt. Ich mag es, dem Code ein bisschen Humor hinzuzufügen, und ich wollte die Hauptkomponente von
GodComponent
. Es ist an der Zeit, den Code zu überprüfen, und ich habe verstanden, warum es so schwierig ist, Namen zu vergeben.
Jeder Code, den ich getauft habe, hat einen impliziten Zweck.
GodComponent
? Dies ist die Komponente, die all den Müll bekommt, den ich nicht an die richtige Stelle bringen möchte. Es enthält alles.
LayoutComponent
es
LayoutComponent
, und in Zukunft würde ich entscheiden, dass diese Komponente ein Layout zuweist. Dass es keinen Zustand enthält.
Eine weitere wichtige Lektion, die ich gelernt habe, war, dass wenn etwas zu groß aussieht, wie eine
LayoutComponent
mit einer Reihe von Geschäftslogiken, es Zeit ist, es
LayoutComponent
, da es hier keine Geschäftslogik geben sollte. Und im Fall des Namens
GodComponent
Vorhandensein von Geschäftslogik keine Rolle.
Müssen Sie die Cluster benennen? Das Aufrufen nach den auf ihnen ausgeführten Diensten ist eine gute Idee, bis Sie auf diesen Clustern etwas anderes ausführen. Wir haben ihnen einen Namen zu Ehren unseres Teams gegeben.
Gleiches gilt für Funktionen.
doEverything()
ist ein schrecklicher Name mit vielen Konsequenzen. Wenn die Funktion alles macht, wird es verdammt schwierig sein, ihre einzelnen Teile zu testen. Egal wie groß diese Funktion werden mag, sie wird Ihnen niemals zu seltsam erscheinen, weil sie alles tun sollte. Also ändere den Namen. Refact.
Ein aussagekräftiger Name hat einen Nachteil. Plötzlich wird der Name zu bedeutungsvoll sein und eine Art Nuance verbergen? Wenn Sie beispielsweise
Sitzungen schließen, wird die Datenbankverbindung nicht geschlossen, wenn
session.close()
in SQLAlchemy aufgerufen wird. Ich hätte die Dokumentation lesen und diesen Fehler verhindern sollen, mehr dazu im Abschnitt über
Fahrräder .
Unter diesem Gesichtspunkt erlaubt es uns die Benennung von Funktionen wie
x
,
y
,
z
anstelle von
count()
,
close()
,
insertIntoDB()
nicht, ihnen eine bestimmte Bedeutung
insertIntoDB()
, und lässt mich sorgfältig überwachen, was diese Funktionen tun.
2Ich hätte nie gedacht, dass ich über die Prinzipien schreiben würde, mehr als eine Textzeile zu benennen.
Geerbter Code und der nächste Entwickler
Haben Sie sich jemals den Code angesehen und es scheint Ihnen seltsam? Warum hast du das geschrieben? Das macht keinen Sinn.
Ich hatte die Möglichkeit, mit einer geerbten Codebasis zu arbeiten. Dies, wissen Sie, mit Kommentaren wie "Kommentieren Sie den Code aus, wenn Muhammad die Situation versteht." Was machst du hier Wer ist Muhammad?
Ich kann die Rollen wechseln und über die Person nachdenken, die dann meinen Code erhält. Wird es ihm seltsam erscheinen? Eine teilweise Überprüfung Ihres Codes hilft Kollegen, Ihren Code zu überprüfen. Dies brachte mich dazu, über den Kontext nachzudenken: Ich muss mich an den Kontext erinnern, in dem mein Team arbeitet.
Wenn ich diesen Code vergesse, später darauf zurückkomme und den Kontext nicht wiederherstellen kann, werde ich sagen: „Was zum Teufel haben sie getan? Das ist dumm ... Ah, warte, ich habe es getan. "
Und hier kommen die Dokumentation und Kommentare im Code ins Spiel.
Dokumentation und Kommentare im Code
Sie helfen, den Kontext aufrechtzuerhalten und Wissen zu vermitteln. Wie Lee in
Wie man gute Software erstellt :
Der Hauptwert von Software liegt nicht im erstellten Code, sondern im Wissen der Personen, die diese Software erstellt haben
Sie haben einen offenen Client-API-Endpunkt, den anscheinend noch niemand verwendet hat. Muss es nur gelöscht werden? Im Allgemeinen ist es eine technische Pflicht. Und wenn ich Ihnen sage, dass in einem der Länder 10 Journalisten einmal im Jahr ihre Berichte an diesen Endpunkt senden? Wie überprüfe ich das? Wenn dies in der Dokumentation nicht erwähnt wird (es war), gibt es keine Möglichkeit, dies zu überprüfen. Wir haben nicht überprüft. Sie entfernten es und einige Monate später kam dieser sehr jährliche Moment. Zehn Journalisten konnten ihre wichtigen Berichte nicht senden, da der Endpunkt nicht mehr vorhanden war. Und Leute mit Produktkenntnissen haben das Team bereits verlassen. Natürlich gibt es jetzt im Code Kommentare, die erklären, warum dies notwendig ist.
Soweit ich weiß, kämpft jedes Team mit der Dokumentation. Und mit der Dokumentation nicht nur nach Code, sondern auch nach den damit verbundenen Prozessen.
Wir haben noch nicht die perfekte Lösung gefunden. Ich persönlich mag die Art und Weise, wie Antirez Kommentare im Code
in verschiedene Arten von Werten unterteilt hat .
Atomic Commits
Wenn Sie ein Rollback durchführen müssen (und es benötigen. Siehe Kapitel
Testen ), ist dieses Commit als einzelnes Modul sinnvoll?
Wie man miesen Code sicher löscht
Es war mir sehr unangenehm, den miesen oder veralteten Code zu löschen. Es schien mir, dass alles, was vor Jahrhunderten geschrieben wurde, heilig ist. Ich dachte: "Sie hatten etwas im Sinn, als sie so schrieben." Dies ist eine Konfrontation zwischen Tradition und Kultur einerseits und Denken im Stil des „Primärprinzips“ andererseits. Dies entspricht der Entfernung des Jahresendpunkts. Ich habe eine besondere Lektion gelernt.
3Ich würde versuchen, den Code zu umgehen, und die führenden Entwickler würden versuchen, ihn zu umgehen. Löschen Sie es. Ein if-Ausdruck, auf den nicht zugegriffen werden kann? Ja, wir löschen. Was habe ich getan Ich habe gerade meine Funktion darüber geschrieben. Ich habe die technische Verschuldung nicht reduziert. Wie auch immer, ich habe gerade die Codekomplexität und die Weiterleitung erhöht. Für die nächste Person wird es noch schwieriger sein, die Bildteile zusammenzusetzen.
Empirisch bin ich zu dem Schluss gekommen: Es gibt einen Code, den Sie nicht verstehen, aber es gibt einen Code, den Sie definitiv nie kontaktieren werden. Löschen Sie den Code, den Sie nicht kontaktieren, und gehen Sie vorsichtig mit dem Code um, den Sie nicht verstehen.
Codeüberprüfung
Eine Codeüberprüfung ist ein großartiges Werkzeug für die Selbstbildung. Dies ist eine externe Rückkopplungsschleife, die zeigt, wie sie den Code schreiben würden und wie Sie ihn geschrieben haben. Was ist der Unterschied? Ein Weg besser als ein anderer? Ich habe mich bei jeder Rezension danach gefragt: "Warum haben sie so geschrieben?" Und wenn er keine passende Antwort finden konnte, ging er und fragte.
Nach dem ersten Monat fing ich an, Fehler im Code meiner Kollegen zu finden (wie sie in meinem gefunden wurden). Es war eine Art Wahnsinn. Die Rezension ist für mich viel interessanter geworden, sie hat sich in ein Spiel verwandelt, das mir gefehlt hat, ein Spiel, das meinen "Sinn für Code" verbessert hat.
Nach meiner Erfahrung müssen Sie den Code erst genehmigen, wenn ich verstanden habe, wie er funktioniert.
Meine Github-Statistik.Testen
Ich habe mich so sehr in das Testen verliebt, dass es für mich unangenehm ist, Code ohne Tests in eine Codebasis zu schreiben.
Wenn Ihre Bewerbung nur eines tut (wie alle meine Schulprojekte), können Sie trotzdem manuell testen.
4 Genau das habe ich getan. Aber was passiert, wenn eine Anwendung 100 verschiedene Aufgaben ausführt? Ich möchte keine halbe Stunde mit Testen verbringen und manchmal verliere ich etwas aus den Augen. Ein Albtraum.
Hier helfen Tests und Testautomatisierung.
Ich behandle Tests als Dokumentation. Dies ist die Dokumentation meiner Ideen zum Code. Tests sagen mir, wie ich (oder jemand anderes vor mir) mir vorstelle, dass der Code funktioniert und wo etwas erwartet wird, das schief gehen sollte.
Wenn ich heute Tests schreibe, versuche ich:
- Zeigen Sie, wie Sie die Testklasse, Funktion oder das System verwenden.
- Zeigen Sie, was meiner Meinung nach schief gehen könnte.
Daher teste ich meistens das Verhalten, aber nicht die Implementierung (
hier ist ein Beispiel , das ich in den Pausen bei Google ausgewählt habe).
In Absatz 2 habe ich die Fehlerquellen nicht erwähnt.
Wenn ich einen Fehler bemerke, stelle ich sicher, dass der Fix einen geeigneten Test hat (dies wird als Regressionstest bezeichnet), um die Informationen zu dokumentieren. Dies ist ein weiterer Grund, warum etwas schief gehen könnte.
5Natürlich verbessert sich die Qualität meines Codes nicht, weil ich Tests schreibe, sondern weil ich Code schreibe. Aber das Lesen der Tests hilft mir, Situationen besser zu verstehen und besseren Code zu schreiben.
Dies ist die allgemeine Situation beim Testen.
Dies ist jedoch nicht die einzige Art von Test, die ich anwende. Ich spreche von Bereitstellungsumgebungen. Möglicherweise haben Sie ideale Komponententests, aber wenn Sie keine Systemtests haben, kann Folgendes passieren:

Dies gilt auch für gut getesteten Code: Wenn Sie nicht über die erforderlichen Bibliotheken auf Ihrem Computer verfügen, stürzt alles ab.
- Es gibt Maschinen, auf denen Sie entwickeln (die Quelle aller Memes wie "Es hat auf meinem Computer funktioniert!").
- Es gibt Maschinen, auf denen Sie testen (möglicherweise stimmen sie mit den vorherigen überein).
- Schließlich gibt es Computer, auf denen Sie bereitstellen (sie sollten nicht mit den Computern übereinstimmen, auf denen Sie entwickelt haben).
Wenn die Test- und Bereitstellungsumgebungen auf den Computern nicht übereinstimmen, treten Probleme auf. Bereitstellungsumgebungen helfen, dies zu vermeiden.
Wir entwickeln in Docker lokal auf unserem Computer.
Wir haben eine Entwicklungsumgebung, diese Computer sind mit einer Reihe von Bibliotheken (und Entwicklungstools) ausgestattet, und hier installieren wir den geschriebenen Code. Hier kann es mit allen notwendigen Systemen getestet werden. Wir haben auch eine Beta / Staging-Umgebung, die die Betriebsumgebung vollständig wiederholt. Schließlich haben wir eine Betriebsumgebung - Maschinen, die Code für unsere Kunden ausführen.
Die Idee ist, Fehler zu erkennen, die beim Testen von Einheiten und Systemen nicht aufgetreten sind. Zum Beispiel der Unterschied in der API zwischen dem anfordernden und dem antwortenden System. Ich denke, bei einem persönlichen Projekt oder einer kleinen Firma kann die Situation völlig anders sein. Nicht jeder hat die Möglichkeit, seine eigene Infrastruktur aufzubauen. Sie können jedoch auf Cloud-Dienste wie AWS und Azure zurückgreifen.
Sie können einzelne Cluster für Entwicklung und Betrieb konfigurieren. AWS ECS verwendet Docker-Images für die Bereitstellung, sodass Prozesse in verschiedenen Umgebungen relativ konsistent sind. Die Integration zwischen verschiedenen AWS-Diensten weist Nuancen auf. Rufen Sie den richtigen Endpunkt aus der richtigen Umgebung auf?
Sie können noch weiter gehen: Laden Sie alternative Container-Images für andere AWS-Services herunter und konfigurieren Sie eine lokale voll funktionsfähige Docker-Compose-basierte Umgebung. Dies beschleunigt die Rückkopplungsschleife.
6 Vielleicht sammle ich mehr Erfahrung, wenn ich mein Nebenprojekt erstelle und starte.
Risikominderung
Welche Schritte können Sie unternehmen, um das Katastrophenrisiko zu verringern? Wenn wir über eine neue radikale Änderung sprechen, wie können wir dann die minimale Ausfallzeit überprüfen, wenn etwas schief geht? "Aufgrund all dieser neuen Änderungen müssen wir das System nicht vollständig bereitstellen." Ist es wahr? Und warum habe ich nicht darüber nachgedacht?
Architektur
Warum spreche ich nach dem Schreiben von Code und Testen über Architektur? Es kann zuerst installiert werden, aber wenn ich nicht in der von mir verwendeten Umgebung programmiert und getestet hätte, wäre es mir wahrscheinlich nicht gelungen, eine Architektur zu erstellen, die die Funktionen dieser Umgebung berücksichtigt.
7
Sie müssen viel nachdenken, wenn Sie eine Architektur erstellen.
- Wie werden Zahlen verwendet?
- Wie viele Benutzer wird es geben? Wie stark kann sich ihre Anzahl erhöhen (die Anzahl der Zeilen in der Datenbank hängt davon ab)?
- Welche Riffe können sich treffen?
Ich muss daraus eine Checkliste mit dem Namen "Claim Collection" machen. Im Moment habe ich nicht genug Erfahrung, ich werde versuchen, es nächstes Jahr bei Bloomberg zu tun. Dieser Prozess steht weitgehend im Widerspruch zu Agile: Inwieweit können Sie die Architektur entwerfen, bevor Sie mit der Implementierung fortfahren? Es geht nur um das Gleichgewicht. Sie müssen entscheiden, wann und was Sie tun möchten. Wann ist es sinnvoll, vorwärts zu eilen und wann - zurückzutreten? Das Sammeln von Anforderungen ist natürlich nicht gleichbedeutend mit der Berücksichtigung aller Probleme. Ich denke, es zahlt sich aus, wenn wir die Entwicklungsprozesse in das Design einbeziehen. Zum Beispiel:
- Wie wird die lokale Entwicklung verlaufen?
- Wie werden wir packen und bereitstellen?
- Wie werden wir End-to-End-Tests durchführen?
- Wie werden wir Stresstests für den neuen Service durchführen?
- Wie werden wir Geheimnisse bewahren?
- CI / CD-Integration?
Wir haben kürzlich eine neue Suchmaschine für
BNEF entwickelt . Es war wunderbar, daran zu arbeiten. Ich organisierte eine lokale Entwicklung und informierte mich über DPG (Pakete und deren Bereitstellung), wobei die Bereitstellung von Geheimnissen erhoben wurde.
Wer hätte gedacht, dass das Bereitstellen von Geheimnissen für einen Stoß so trivial sein könnte:
- Sie können nicht in Code eingefügt werden, da jemand sie bemerken kann
- Um sie als Umgebungsvariable zu speichern, da die Spezifikation 12 Anwendungsfaktoren bietet? Keine schlechte Idee, aber wie soll man sie dort hinstellen? (Es ist schmerzhaft, jedes Mal, wenn das Auto startet, zum Produkt zu gehen, um Umgebungsvariablen einzugeben.)
- Als Dateien bereitstellen? Aber woher kommen sie und wie sollen sie gefüllt werden?
Wir wollen nicht alles manuell machen.
Als Ergebnis kamen wir zu einer Datenbank mit rollenbasierter Zugriffskontrolle (nur wir und unsere Computer können mit der Datenbank kommunizieren). Unser Code erhält beim Start Geheimnisse aus der Datenbank. Dieser Ansatz lässt sich im Rahmen von Entwicklungs-, Staging- und Betriebsumgebungen gut replizieren. Geheimnisse werden in geeigneten Datenbanken gespeichert.
Auch bei Cloud-Diensten wie AWS kann die Situation völlig anders sein. Sie müssen sich irgendwie nicht um Geheimnisse kümmern. Erstellen Sie ein Konto für Ihre Rolle, geben Sie die Geheimnisse in die Benutzeroberfläche ein, und Ihr Code findet sie, wenn sie benötigt werden. Dies vereinfacht alles erheblich, aber ich bin froh, dass ich Erfahrungen gesammelt habe, dank derer ich diese Einfachheit schätzen kann.
Wir schaffen Architektur, ohne die Wartung zu vergessen
Das Systemdesign ist inspirierend. Und die Eskorte? Nicht zu viel. Meine Reise durch die Welt der Escorts führte mich zu der Frage: Warum und wie verschlechtern sich Systeme? Der erste Teil der Antwort bezieht sich nicht auf die Stilllegung aller veralteten, sondern nur auf die Hinzufügung einer neuen. Die Tendenz zum Hinzufügen statt Löschen (erinnert es nichts daran?). Der zweite Teil ist das Entwerfen mit dem ultimativen Ziel. Ein System, das im Laufe der Zeit beginnt, das zu tun, wofür es nicht vorgesehen war, funktioniert nicht unbedingt so gut wie ein System, das ursprünglich für dieselben Aufgaben entwickelt wurde. Dies ist ein Step-Back-Ansatz, keine Tricks und Tricks.
Ich kenne mindestens drei Möglichkeiten, um die Abbaurate zu verringern.
- Separate Geschäftslogik und Infrastruktur: Die Infrastruktur verschlechtert sich normalerweise - die Last steigt, Frameworks werden veraltet, Zero-Day-Schwachstellen treten auf usw.
- Erstellen Sie Prozesse für die zukünftige Unterstützung. Wenden Sie die gleichen Updates für alte und neue Bits an. Dies verhindert Unterschiede zwischen dem Alten und dem Neuen und hält den gesamten Code in einem „modernen“ Zustand.
- Stellen Sie sicher, dass Sie alles wegwerfen, was unnötig und alt ist.
Bereitstellung
Werde ich Features zusammenpacken oder einzeln bereitstellen? Wenn Sie sie je nach aktuellem Prozess zusammenpacken, warten Sie auf Probleme. Fragen Sie sich, warum Sie Funktionen zusammenpacken möchten?
- Die Bereitstellung nimmt viel Zeit in Anspruch?
- Macht Codeüberprüfung nicht zu viel Spaß?
Was auch immer der Grund sein mag, diese Situation muss korrigiert werden. Ich kenne mindestens zwei Probleme im Zusammenhang mit der Verpackung:
- Sie selbst blockieren alle Funktionen, wenn eine davon einen Fehler aufweist.
- Sie erhöhen das Risiko von Problemen.
Unabhängig davon, für welchen Bereitstellungsprozess Sie sich entscheiden, möchten Sie immer, dass Ihre Autos wie Vieh und nicht wie Haustiere sind. Sie sind nicht einzigartig. Sie wissen genau, was auf jeder Maschine ausgeführt wird und wie Sie sie im Todesfall neu erstellen können. Sie werden nicht verärgert sein, wenn ein Auto stirbt, Sie holen einfach ein neues ab. Sie weiden sie, nicht wachsen.
Wenn etwas schief geht
Für den Fall, dass etwas schief geht - und das tut es auch - gibt es eine goldene Regel: Minimieren Sie die Auswirkungen auf die Kunden. Im Falle von Fehlern war mein erster Wunsch immer, das Problem zu beheben. Dies scheint nicht die optimale Lösung zu sein. Anstatt zu reparieren, müssen Sie zuerst ein Rollback durchführen, auch wenn dies in einer Zeile möglich ist. Zum vorherigen Betriebszustand zurückkehren. Dies ist der schnellste Weg, um Kunden zu einer funktionierenden Version zurückzukehren. Erst dann finde ich heraus, wo das Problem liegt und behebe es.
Gleiches gilt für den „beschädigten“ Computer in Ihrem Cluster: Schalten Sie ihn aus, markieren Sie ihn als unzugänglich, bevor Sie herausfinden, was mit ihm passiert ist. Ich finde es seltsam, wie sehr mein natürliches Verlangen und mein Instinkt der optimalen Lösung widersprechen.
Ich denke, dieser Instinkt hat auch dazu geführt, dass ich Fehler länger behoben habe. Manchmal wurde mir klar, dass etwas nicht funktionierte, weil der Code, den ich schrieb, irgendwie falsch war, und ich stieg in die Wildnis und schaute auf jede Zeile. So etwas wie eine Suche "zuerst in die Tiefe". Und als sich herausstellte, dass das Problem aufgrund einer Konfigurationsänderung auftrat, das heißt, ich habe es überhaupt nicht überprüft, beunruhigte mich diese Situation. Ich habe viel Zeit damit verschwendet, nach einem Fehler zu suchen.
Seitdem habe ich gelernt, "zuerst in der Breite" und daher bereits "zuerst in der Tiefe" zu suchen, um übergeordnete Gründe auszuschließen. Was genau kann ich mit aktuellen Ressourcen bestätigen?
- Funktioniert das Auto?
- Ist der Code korrekt installiert?
- Gibt es eine Konfiguration?
- <Codespezifische Konfiguration>, z. B. ob das Routing richtig geschrieben ist?
- Ist die Schemaversion korrekt?
- Und dann tauche ich in den Code ein.
Wir dachten, dass nginx falsch installiert wurde, aber es stellte sich heraus, dass nur die Konfiguration deaktiviert war
Natürlich muss ich das nicht jedes Mal tun. Manchmal reicht schon eine Fehlermeldung aus, um sofort zum Code zu gelangen. Wenn ich die Ursache nicht ermitteln kann, versuche ich, die Anzahl der Codeänderungen zu minimieren, um den Grund zu finden. Je weniger Änderungen vorgenommen werden, desto schneller kann ich die eigentliche Wurzel des Problems finden. Außerdem habe ich jetzt ein Memo für Fehler, das mir mehr als eine Stunde Zeit gespart hat, als ich dachte: "Was habe ich vermisst?" Manchmal vergesse ich die einfachsten Überprüfungen wie das Konfigurieren des Routings, das Übereinstimmen von Schema- und Dienstversionen usw. Dies ist ein weiterer Schritt in der Entwicklung des von mir verwendeten Technologie-Stacks. Was Sie nur mit Erfahrung gewinnen, ist die Intuition, festzustellen, was genau nicht funktioniert.
Fahrrad
Dieser Artikel kann ohne eine Geschichte nicht vollständig sein. , . SQLAlchemy. BNEF , . . SQLAlchemy, , Solr. - .
«MYSQL server has gone away.» . , , . , . , . , ?
, ? , , . , ,
__exit__()
session.close()
.
, , . . . . .
Session.close()
MySQL- SQLAlchemy , NullPool. . , , . : StackOverflow (, !) , , SQL- . , . , , (), .
«» , 1 8. , , — .
, .
Überwachung
, . , , . , .
, , , . , . , . «
?! , ? ".
, : , . , , . , , . , . — ? , , . . , -, , , , , .. .
. , , ? (, AWS CloudWatch Grafana). .
. , , 50 %, — . ? . , — (, ?).
. , , , , ? ? , ?
, . , . — - .
Fazit
. , , , . , - !
. , !
, . , — How to Build Good Software .
. : ! , .
- ?
- ? , ? , ?
- . , ? ?
- (utils) (, , , ) , « »?
- ?
- , , - ?
- — API , ?
- ? , .
- , , . , , .
- PR: « , , 52 , , , , , ». ?
- . ?
- ?
- ?
Anmerkungen
- . , ? - ? , ?
- ,
x()
, y()
z(),
x()
, y()
z()
. , WYSIATI .
- .
- . , ?
- , - , , . , .
- , , Docker- AWS.
- .