Andrey Nikolsky, der Direktor für den Betrieb des Portals Banki.ru, sprach auf der letztjährigen Konferenz über Orphan-Services: wie man ein Waisenkind in der Infrastruktur identifiziert, was an Orphan-Services schlecht ist, was man damit machen sollte und wie man vorgeht, wenn nichts hilft.
Hier ist die schriftliche Version des Vortrags.

Guten Tag, Kollegen! Mein Name ist Andrey und ich leite den Betrieb bei Banki.ru.
Wir haben große Services, das sind sogenannte Monolith-Services, es gibt Services im klassischen Sinne und es gibt auch ganz kleine. In meiner praktischen Terminologie sage ich, wenn ein Service einfach und klein ist, dann ist er mikro, und wenn er nicht ganz einfach und nicht klein ist, dann ist es einfach ein Service.
Vorteile der Services
Ich gehe schnell durch die Vorteile der Services.

Erstens – Skalierbarkeit. Sie können schnell etwas auf dem Service machen und in die Produktion starten. Wenn der Traffic kommt, klonen Sie den Service. Wenn noch mehr Traffic kommt, klonen Sie noch einmal und leben damit. Das ist ein guter Bonus und, grundsätzlich, als wir angefangen haben, galt er als der wichtigste Grund, warum wir all das tun.

Zweitens gibt es die isolierte Entwicklung, wenn Sie mehrere Entwicklungsteams haben, mit verschiedenen Entwicklern in jedem Team, und jedes Team arbeitet an einem eigenen Service.
Es gibt einige Nuancen mit den Teams. Die Entwickler sind unterschiedlich. Beispielsweise gibt es . Ich habe das zum ersten Mal bei Maxim Dorofeev gesehen. Manchmal gibt es Schneeflocken-Menschen in bestimmten Teams und in anderen nicht. Das führt dazu, dass die verschiedenen in der Firma genutzten Services etwas ungleichmäßig sind.

Schauen Sie sich das Bild an: Das ist ein guter Entwickler, er hat große Hände und kann viel machen. Das Hauptproblem ist, woher diese Hände kommen.

Die Services ermöglichen es, verschiedene Programmiersprachen zu verwenden, die besser für unterschiedliche Aufgaben geeignet sind. Ein Service ist in Go, ein anderer in Erlang, einer in Ruby, einer in PHP, und einer in Python. Man kann sich also ziemlich weit ausbreiten. Auch hier gibt es Nuancen.

Service-orientierte Architektur bedeutet in erster Linie DevOps. Das heißt, wenn Sie keine Automatisierung haben, keinen Deploy-Prozess, wenn Sie manuell konfigurieren, können sich Ihre Konfigurationen von Instanz zu Instanz unterscheiden, und Sie müssen jedes Mal etwas anpassen, dann befinden Sie sich im Chaos.
Zum Beispiel, wenn Sie 20 Dienste haben und alles manuell deployen müssen, haben Sie 20 Konsolen und drücken gleichzeitig ‚Enter‘ wie ein Ninja. Das ist nicht gerade optimal.
Wenn Ihr Service nach dem Testen (sofern es Tests gibt) noch nachjustiert werden muss, um in der Produktion zu funktionieren, habe ich auch schlechte Nachrichten für Sie.
Wenn Sie auf spezialisierte Amazon-Dienste angewiesen sind und dabei in Russland arbeiten, dann war vor zwei Monaten auch bei Ihnen alles ‚Alles brennt, ich bin in Ordnung, alles ist toll‘.

Wir verwenden Ansible zur Automatisierung der Bereitstellung, Puppet für die Konvergenz, Bamboo für die Automatisierung des Deployments und Confluence, um all dies zu dokumentieren.
Ich werde nicht näher darauf eingehen, da der Vortrag eher über Praktiken der Zusammenarbeit und nicht über technische Implementierungen handelt.

Manchmal hatten wir Probleme, zum Beispiel wenn Puppet auf dem Server mit Ruby 2 läuft, aber eine Anwendung für Ruby 1.8 geschrieben ist. Dann funktioniert es nicht zusammen. Da tritt ein Problem auf. Wenn Sie mehrere Ruby-Versionen auf einer Maschine benötigen, entstehen normalerweise Schwierigkeiten.
Wir stellen beispielsweise jedem Entwickler eine Umgebung bereit, in der alles, was wir haben, verfügbar ist – alle Dienste, die entwickelt werden können. So hat er ein isoliertes Umfeld, in dem er experimentieren und aufbauen kann, wie er möchte.
Es kann notwendig sein, ein speziell kompiliertes Paket mit Unterstützung für bestimmte Funktionen zu benötigen. Das kann recht kompliziert sein. Ich habe einen Vortrag gehört, in dem das Docker-Image 45 GB groß war. In Linux ist es natürlich einfacher, da ist alles etwas kleiner, aber dennoch reicht der Platz oft nicht aus.
Und es gibt manchmal widersprüchliche Abhängigkeiten, wenn ein Teil des Projekts von einer Bibliothek in einer Version abhängt und ein anderer Teil von einer anderen Version, und die Bibliotheken sich überhaupt nicht miteinander kombinieren lassen.

Wir haben Websites und Dienste auf PHP 5.6, dafür schämen wir uns, aber was soll man machen. Das ist unsere eine Plattform. Es gibt mehr Websites und Dienste auf PHP 7, für die schämen wir uns nicht. Und jeder Entwickler hat sein eigenes kleines Repository, wo er fröhlich programmiert.
Wenn Sie in Ihrem Unternehmen in einer Sprache schreiben, klingt es normal, drei virtuelle Maschinen pro Entwickler zu haben. Wenn Sie verschiedene Programmiersprachen verwenden, wird die Situation komplizierter.

Sie haben Websites und Dienste auf dieser, und dann auf dieser, dann kommt eine weitere Plattform für Go, eine Plattform für Ruby, und noch irgendein Redis daneben. Am Ende verwandelt sich das alles in ein großes Supportfeld, und ständig kann etwas davon kaputtgehen.

Deshalb haben wir die Vorteile der Programmiersprache gegen die Nutzung verschiedener Frameworks eingetauscht, da die Frameworks in PHP ziemlich unterschiedlich sind, sie haben verschiedene Möglichkeiten, unterschiedliche Communities und unterschiedliche Unterstützung. Und man kann den Dienst so entwickeln, dass man schon etwas Fertiges dafür hat.
Jeder Dienst hat sein eigenes Team.

Ein großer Vorteil, der sich über die Jahre herauskristallisiert hat, ist, dass jedes Service-Team eigenständig ist. Das ist besonders praktisch für große Projekte, da Sie Zeit bei der Dokumentation sparen und die Manager ihr Projekt gut kennen.
Aufgaben aus dem Support können hervorragend delegiert werden. Zum Beispiel, wenn ein Versicherungsservice ausfällt, kann sofort das Team, das für die Versicherung zuständig ist, eingreifen und das Problem beheben.
Neue Features werden schnell umgesetzt, weil, wenn Sie einen bestimmten, atomaren Service haben, Änderungen rasch integriert werden können.
Wenn Ihr Service einmal ausfällt – was unvermeidlich passiert – hat das keine Auswirkungen auf die anderen Services. Die Entwickler anderer Teams kommen nicht mit Baseballschlägern zu Ihnen und sagen: „Oh je, das sollten Sie besser nicht tun“.

Natürlich gibt es auch Nuancen. Wir haben feste Teams, die an ihre Manager gebunden sind. Es gibt klare Dokumente, und die Manager achten sorgfältig darauf. Jedes Team hat mehrere Services in Verantwortung und eine spezifische Kompetenz.
Wenn die Teams flexibel sind (was bei uns manchmal der Fall ist), verwenden wir eine gute Methode, die als „Sternenkarte“ bezeichnet wird.

Sie haben eine Liste von Diensten und Personen. Ein Sternchen bedeutet, dass die Person ein Experte in diesem Dienst ist, und ein Buchsymbol zeigt an, dass die Person diesen Dienst studiert. Die Aufgabe der Person ist es, das Buchsymbol gegen ein Sternchen einzutauschen. Wenn hingegen nichts neben dem Dienst steht, gibt es Probleme, über die ich später sprechen werde.
Wie entstehen ausgesetzte Dienste?

Das erste Problem, die erste Möglichkeit, einen ausgesetzten Dienst in Ihrer Infrastruktur zu bekommen, sind Entlassungen. Hat jemand schon einmal erlebt, dass Fristen aus dem Geschäft kommen, bevor die Aufgaben bewertet wurden? Manchmal sind die Fristen sehr eng und es bleibt einfach keine Zeit für die Dokumentation. 'Der Dienst muss in die Produktion gehen, später werden wir es ergänzen.'
Wenn das Team klein ist, gibt es manchmal nur einen Entwickler, der alles schreibt, während die anderen unterstützen. «Ich habe die Hauptarchitektur entworfen, du kannst die Schnittstellen erstellen.» Dann verlässt zu einem gewissen Zeitpunkt der Manager beispielsweise das Team. In dieser Zeit, in der der Manager gegangen ist und noch kein neuer ernannt wurde, entscheiden die Entwickler selbst, in welche Richtung sich der Service entwickelt und was dort passiert. Wie wir wissen (lassen Sie uns ein paar Folien zurückspringen), gibt es in einigen Teams sogenannte Einzelgänger, manchmal ist ein Einzelgänger-Teamleiter. Wenn dieser dann das Unternehmen verlässt, haben wir einen „waisen“ Service.

Die Aufgaben von Support und Business verschwinden dabei nicht, sondern sammeln sich im Backlog. Wenn es bei der Entwicklung des Services architektonische Fehler gab, bleiben diese ebenfalls im Backlog. Der Service degradiert langsam.
Wie erkennt man einen „Waisen-Service“?
Diese Liste beschreibt die Situation recht gut. Wer erkennt etwas Ähnliches in seiner Infrastruktur?

Zu den dokumentierten Workarounds: Es gibt einen Service, der im Wesentlichen funktioniert, und es gibt ein zweiseitiges Handbuch, wie man damit umgeht, aber wie er intern funktioniert, weiß niemand.
Oder zum Beispiel gibt es einen Link-Verkürzer. Momentan verwenden wir drei verschiedene Link-Verkürzer für unterschiedliche Zwecke in verschiedenen Diensten. Das sind die Folgen.

Jetzt werde ich zum Kapitän der Offensichtlichkeit. Was muss unternommen werden? Zunächst einmal sollte der Dienst an einen anderen Manager oder ein anderes Team übergeben werden. Wenn Ihr Teamleiter noch nicht gekündigt hat, sollten Sie jemanden in dieses andere Team einbeziehen, der wenigstens etwas darüber versteht, wenn Sie merken, dass der Dienst wie ein Waisenkind behandelt wird.
Das Wichtigste: Sie müssen über eindeutig dokumentierte Übergabeverfahren verfügen. In unserem Fall überwache ich das in der Regel, da ich möchte, dass alles funktioniert. Die Manager wollen, dass es schnell übergeben wird, und was danach passiert, ist ihnen nicht so wichtig.

Eine weitere Möglichkeit, ein Waisenkind zu schaffen, besteht darin, es 'auszulagern, das geht schneller, und später übergeben wir es dem Team'. Es ist klar, dass jeder in der Mannschaft seine eigenen Pläne und Warteschlangen hat. Oft denkt der Geschäftskunde, dass der Outsourcing-Anbieter dies genauso gut macht wie die Technikabteilung des Unternehmens. Dabei haben sie unterschiedliche Motivationen. Beim Outsourcing gibt es oft seltsame technologische und algorithmische Lösungen.

Wir hatten zum Beispiel einen Service, bei dem Sphinx an verschiedenen unerwarteten Orten vorkam. Ich werde später erzählen, was wir tun mussten.
Outsourcing-Anbieter haben oft selbstgeschriebene Frameworks. Das ist einfach nur nacktes PHP mit Copy-Paste vom vorherigen Projekt, wo man allerlei findet. Große Stolpersteine in den Deploy-Skripten, wenn man mit komplizierten Bash-Skripten mehrere Zeilen in einer Datei ändern muss, während diese Deploy-Skripte von einem anderen Skript aufgerufen werden. Am Ende ändert man das Deployment-System, wählt etwas anderes und plötzlich funktioniert der Service nicht mehr. Weil man noch 8 Links zwischen verschiedenen Ordnern setzen musste. Oder manchmal funktioniert eine Tausend Zeilen, aber bei hunderttausend läuft nichts mehr.
Ich werde weiterhin das Steuer übernehmen. Die Abnahme eines Outsourcing-Services ist ein notwendiger Prozess. Hat jemand schon mal erlebt, dass ein Service aus dem Outsourcing kommt und nirgendwo angenommen wird? Das ist zwar nicht so oft wie bei einem Waise-Service, aber doch.

Dienste müssen überprüft und bewertet werden, Passwörter sollten geändert werden. Es gab einen Fall, in dem uns ein Dienst angeboten wurde, bei dem im Code stand: "if login == 'admin' && password == 'admin'..." Denken wir darüber nach: Schreiben Menschen sowas im Jahr 2018?
Die Überprüfung des Speicherplatzes ist ebenfalls wichtig. Es ist notwendig zu sehen, was bei hunderttausend Datensätzen passieren wird, noch bevor Sie diesen Dienst in die Produktion bringen.

Es sollte nicht beschämend sein, einen Dienst zur Überarbeitung zurückzusenden. Wenn Sie sagen: "Wir werden diesen Dienst nicht akzeptieren, wir haben 20 Aufgaben, erledigen Sie diese, dann akzeptieren wir", ist das völlig normal. Ihr Gewissen sollte nicht belastet sein, weil Sie einen Manager in Schwierigkeiten bringen oder das Unternehmen Geld kosten. Das Unternehmen wird später noch mehr ausgeben.
Es gab einen Fall, als wir beschlossen, ein Pilotprojekt im Outsourcing durchzuführen.

Es wurde pünktlich geliefert, und das war das einzige Qualitätskriterium. Deshalb wurde ein weiteres Pilotprojekt ins Leben gerufen, das eigentlich kein echtes Pilotprojekt mehr war. Diese Dienste wurden übernommen, und administrativ wurde gesagt: 'Hier ist euer Code, hier ist das Team, hier ist euer Manager.' Die Dienste haben bereits angefangen, Gewinne zu erzielen. Faktisch sind sie jedoch immer noch wie Waisen, niemand versteht, wie sie funktionieren, und die Manager distanzieren sich bewusst von ihren Aufgaben.

Ein weiteres großartiges Konzept ist die Guerilla-Entwicklung. Wenn eine Abteilung, gewöhnlich die Marketingabteilung, eine Hypothese testen möchte, beauftragt sie den gesamten Service an einen Dienstleister. Es kommt Verkehr auf die Seite, sie schließen die Unterlagen ab, unterschreiben die Verträge mit dem Auftragnehmer, gehen in Betrieb und sagen: 'Leute, wir haben hier einen Service, der bereits Traffic hat und uns Geld einbringt, lasst ihn uns übernehmen.' Wir sind dann: 'Wow, wie kann das sein?'

Und ein weiterer Weg, um einen Orphan-Service zu erhalten: Wenn ein Team plötzlich überlastet ist, sagt die Leitung: „Lass uns den Service von diesem Team zu einem anderen übertragen, das weniger Auslastung hat“. Dann geben wir ihn an das dritte Team weiter und wechseln den Manager. Am Ende haben wir wieder einen Waisenservice.
Was ist das Problem mit Waisenservices?

Wer es nicht weiß, das ist das in Schweden gebaute Schlachtschiff Wasa, berühmt dafür, dass es fünf Minuten nach seiner Indienststellung sank. Und der König von Schweden hat übrigens dafür niemanden hingerichtet. Es wurde von zwei Generationen von Ingenieuren gebaut, die nicht wussten, wie man solche Schiffe baut. Ein vorhersehbarer Effekt.
Das Schiff hätte übrigens viel schlimmer sinken können, z. B. wenn der König schon bei einem Sturm darauf unterwegs gewesen wäre. Aber so ist es sofort gesunken, was im Agile gut ist – früh scheitern.
Wenn wir früh gescheitert sind, gibt es normalerweise keine Probleme. Zum Beispiel während der Abnahme wurde es zurÜberarbeitung geschickt. Wenn wir jedoch bereits im Produktionsumfeld gescheitert sind, in das Geld investiert wurde, können Probleme auftauchen. Die Folgen, wie es im Geschäftsleben genannt wird.
Warum sind Waisenservices gefährlich:
- Der Service kann plötzlich ausfallen.
- Der Service wird lange repariert oder es erfolgt überhaupt keine Reparatur.
- Sicherheitsprobleme.
- Probleme mit Anpassungen und Updates.
- Wenn ein wichtiger Dienst ausfällt, leidet der Ruf des Unternehmens.
Was ist mit verwaisten Diensten zu tun?

Ich wiederhole, was zu tun ist. Zunächst einmal muss es Dokumentation geben. Sieben Jahre bei Banki.ru haben mich gelehrt, dass Tester den Entwicklern nicht einfach glauben sollten, und die Operationsabteilung sollte auch niemandem blind vertrauen. Man muss überprüfen.

Zweitens sollten Interaktionsschemata erstellt werden. Denn es kommt vor, dass Dienste, die nicht besonders gut angenommen werden, Abhängigkeiten enthalten, die niemand erwähnt hat. Zum Beispiel haben die Entwickler einen Dienst mit ihrem Schlüssel für andere Dienste wie Yandex.Maps oder Dadata verknüpft. Wenn Ihr kostenloses Kontingent aufgebraucht ist, bricht alles zusammen, und Sie wissen nicht, was passiert ist. Solche Stolpersteine müssen dokumentiert werden: Im Dienst wird Dadata, Sms oder etwas anderes verwendet.

Drittens, Umgang mit technischem Schulden. Wenn Sie irgendwelche Provisorien machen oder einen Dienst übernehmen und sagen, dass etwas getan werden muss, müssen Sie darauf achten, dass dies auch umgesetzt wird. Denn später kann sich herausstellen, dass ein kleines Loch nicht so klein ist, und Sie könnten hineinfallen.
Bei architektonischen Herausforderungen hatten wir eine Geschichte mit Sphinx. In einem unserer Dienste wurde Sphinx verwendet, um Listen zu erstellen. Einfach eine Liste mit Pagination, die jedoch jede Nacht neu indiziert wurde. Sie bestand aus zwei Indizes: ein großer, der jede Nacht indiziert wurde, und ein kleiner, der hinzugefügt wurde. Jeden Tag, mit einer Wahrscheinlichkeit von 50%, gab es entweder ein Problem oder nicht; beim Aktualisieren wurde der Index beschädigt, und die Nachrichten hörten auf, auf der Hauptseite aktualisiert zu werden. Zunächst dauerte es 5 Minuten, bis der Index neu indiziert war. Mit der Zeit wuchs der Index und irgendwann begann die Neuindizierung 40 Minuten in Anspruch zu nehmen. Als wir das beheben konnten, waren wir erleichtert, denn es war klar, dass es nicht lange dauern würde, bis unser Index den ganzen Arbeitstag lang neu indiziert werden würde. Das wäre ein Versagen für unser Portal gewesen — acht Stunden ohne Nachrichten, und unser Geschäft stünde still.
Arbeitsplan für den Dienst, der keine Begleitung hat

Es ist tatsächlich sehr schwierig, das zu tun, denn DevOps bedeutet Kommunikation. Man möchte gute Beziehungen zu seinen Kollegen pflegen, aber wenn man seine Kollegen und Manager mit Vorschriften überhäuft, können sie gemischte Gefühle gegenüber denen entwickeln, die das tun.
Neben all diesen Punkten gibt es noch etwas Wichtiges: Für jeden spezifischen Dienst und jeden bestimmten Teil des Deployments sollten konkrete Personen verantwortlich sein. Wenn es keine Personen gibt und man andere Leute heranziehen muss, um sich darin einzuarbeiten, wird es schwierig.

Wenn all das nicht geholfen hat und dein Service weiterhin ohne Betreuung dasteht, niemand ihn übernehmen möchte, die Dokumentation nicht geschrieben wird und das Team, das zu diesem Service gerufen wurde, sich weigert, etwas zu tun, gibt es einen einfachen Weg — alles neu machen.
Das bedeutet, du nimmst die Anforderungen an den Dienst erneut zur Hand und schreibst einen neuen, besseren Dienst auf einer besseren Plattform, ohne seltsame technische Lösungen. Und du migrierst in einem laufenden Betrieb darauf.

Wir hatten die Situation, dass wir einen Service auf Yii 1 in Anspruch nahmen und realisierten, dass wir ihn nicht weiterentwickeln konnten, da uns die Entwickler ausgegangen waren, die gut auf Yii 1 programmieren konnten. Alle Entwickler sind jetzt gut mit Symfony 3 vertraut. Was sollen wir tun? Wir haben Zeit, ein Team und einen Manager bereitgestellt, das Projekt neu geschrieben und den Traffic schrittweise darauf umgeleitet.
Nachdem der alte Service entfernt werden kann. Das ist mein Lieblingsverfahren, wenn ich in der Konfigurationsverwaltung einen Service löschen und dann sicherstellen kann, dass alle Produktionsmaschinen stillgelegt sind, sodass keine Spuren für die Entwickler zurückbleiben. Das Repository bleibt im Git erhalten.
Das ist alles, was ich sagen wollte, und ich bin bereit zu diskutieren; das Thema ist umstritten, viele haben damit Erfahrungen gesammelt.
In den Folien ging es darum, dass Sie die Programmiersprachen vereinheitlicht haben. Als Beispiel wurde das Bearbeiten von Bildern genannt. Ist es wirklich notwendig, auf eine einzige Sprache zu bestehen? Denn das Resize von Bildern in PHP hätte man tatsächlich auch mit Golang machen können.
Tatsächlich ist es nicht unbedingt notwendig, wie alle Praktiken. In manchen Fällen kann es sogar unerwünscht sein. Aber man muss verstehen, dass wenn Sie in Ihrem Unternehmen 50 Mitarbeiter im technischen Bereich haben, von denen 45 PHP-Entwickler sind, weitere 3 DevOps-Experten, die mit Python, Ansible und Puppet arbeiten, und nur einer von ihnen einen Service zum Resizen von Bildern in Go schreibt, dann geht die Expertise mit ihm, wenn er geht. Und gleichzeitig müssen Sie einen spezifischen Entwickler auf dem Markt suchen, der diese Sprache beherrscht, besonders wenn sie selten ist. Aus organisatorischer Sicht ist das problematisch. Aus der Sicht eines DevOps benötigen Sie nicht nur eine Kopie eines vorhandenen Sets von Playbooks, die Sie verwenden, um Services bereitzustellen, sondern müssen diese von Grund auf neu schreiben.
Wir entwickeln gerade einen Service mit Node.js, und das wird genau die Plattform für jeden Entwickler mit einer eigenen Sprache sein. Aber wir haben darüber nachgedacht, dass es sich lohnt. Es ist also eine Frage, bei der man sich hinsetzen und nachdenken sollte.
Wie überwachen Sie Ihre Services? Wie sammeln und verfolgen Sie Logs?
Wir sammeln die Logs in Elasticsearch und speichern sie in Kibana. Je nachdem, ob es sich um eine Produktions- oder Testumgebung handelt, verwenden wir unterschiedliche Log-Collector. Manchmal nutzen wir Lumberjack, manchmal etwas anderes, ich kann mich nicht mehr genau erinnern. Außerdem gibt es bestimmte Stellen in bestimmten Services, wo wir Telegraf installieren und die Logs separat woanders hin schicken.
Wie lebt man mit Puppet und Ansible in einer Umgebung zusammen?
Tatsächlich haben wir derzeit zwei Umgebungen: eine mit Puppet und eine mit Ansible. Wir arbeiten daran, sie zu hybridisieren. Ansible eignet sich gut für die Erstkonfiguration, während Puppet nicht ideal dafür ist, da es manuelles Eingreifen direkt in die Infrastruktur erfordert. Puppet stellt jedoch die Konvergenz der Konfiguration sicher. Das bedeutet, dass die Infrastruktur selbstständig auf dem aktuellen Stand bleibt. Damit eine Ansible-verwaltete Maschine aktuell bleibt, müssen wir ständig Playbooks in regelmäßigen Abständen ausführen. Das ist der entscheidende Unterschied.
Wie gewährleisten Sie die Kompatibilität? Haben Sie Konfigurationen sowohl in Ansible als auch in Puppet?
Es ist ein ständiges Ärgernis für uns. Wir arbeiten an der Kompatibilität und überlegen, wie wir all das am besten bewältigen können. Puppet installiert Pakete und verwaltet einige Verknüpfungen, während Ansible beispielsweise den Code implementiert und die aktuellen Konfigurationen der Anwendungen anpasst.
In der Präsentation ging es um verschiedene Versionen von Ruby. Was ist die Lösung?
Wir sind damit an einer Stelle in Berührung gekommen und müssen ständig daran denken. Wir haben einfach den Teil deaktiviert, der auf der Ruby-Version lief, die nicht mit den Anwendungen kompatibel war, und haben ihn separat gehalten.
In diesem Jahr findet die Konferenz am 7. Dezember im „Technopolis“ statt. Wir nehmen bis zum 11. November Vorschläge für Vorträge an. uns, wenn Sie einen Vortrag halten möchten.
Die Registrierung für Teilnehmer ist geöffnet, schließen Sie sich uns an!
Quelle: habr.com
