Leben vor der Laufzeit. Yandex-Bericht

In einem großen Projekt kann sich die Aufgabe ergeben, Änderungen für den Endbenutzer anhand von Unterschieden im Front-End-Code der Anwendung zu identifizieren. Der Entwickler von Yandex.Market Nikita Sidorov @nickshevr erzählte, wie wir dieses Problem mithilfe der Diffector-Bibliothek gelöst haben, wie das Moduldiagramm in Node.js-Anwendungen erstellt und analysiert wurde und wie Fehler im Code vor dem Start gefunden wurden.



- Heute werde ich versuchen, so offen wie möglich mit Ihnen umzugehen. Ich arbeite seit etwas mehr als anderthalb Jahren in Yandex.Market. Ich mache die gleiche Menge an Web und habe angefangen, Änderungen an mir selbst zu bemerken. Sie können sie auch bemerken. Meine durchschnittliche Haarlänge nahm zu und der Bart begann zu erscheinen. Und wissen Sie, heute habe ich meine Kollegen angeschaut: bei Sergei Berezhnoy veged , bei Vova Grinenko tadatuta , und mir wurde klar, dass dies ein gutes Kriterium für mich ist, um als echter Front-End-Entwickler fast gereift zu sein.

Und als ich zu diesem Fluchen kam, beschloss ich, mit Ihnen über das Leben zu sprechen, über das, an dem wir alle teilnehmen. Hauptsächlich über das Leben vor der Laufzeit. Jetzt werde ich erklären, worum es bei alledem geht.



Was ist mit dem Leben? Natürlich über das Leben des Codes. Code ist das, was wir tun. Ich möchte Sie daran erinnern, dass ich mich entschlossen habe, hier aufrichtig mit Ihnen umzugehen, damit die erste Folie so einfach wie möglich war. Ich habe die Wahrheit genommen, die erste Stufe - die Adoption, sehen Sie, niemand wird mit diesem Axiom streiten.



Und dann wurde mir klar, dass ich es ändern musste, aber damit es klar war. Lassen Sie es eine Art Akzeptanz von Anforderungen sein. Jeder Code beginnt mit der Tatsache, dass Sie sich die Aufgabe ansehen und versuchen, die Anforderungen zu akzeptieren, die Sie gestellt haben.



Danach beginnen wir natürlich mit dem Schreiben - wir schreiben unseren Code. Dann decken wir es mit Tests ab, wir überprüfen seine Wirksamkeit selbst. Danach prüfen wir bereits, ob unsere Anwendung mit unserem gesamten Code funktioniert. Danach geben wir es dem Tester - lassen Sie ihn überprüfen. Was denkst du danach? Ich erinnere dich, Leben vor der Laufzeit. Denken Sie, dass die Laufzeit darauf folgt? In der Tat stellt sich heraus, dass. Und das ist kein Fehler in der Präsentation. Sehr oft haben Sie in jeder Phase der Überprüfung - und es kann viel mehr geben, als ich angegeben habe - einen Anruf, um erneut zu schreiben. Stimmen Sie zu, dies kann ein ziemlich großes Problem sein. Dies kann die Bereitstellung einiger Funktionen in der Produktion verlangsamen und Sie als Entwickler im Prinzip verlangsamen, da das Ticket an Ihnen hängen bleibt. Und hier geht alles vorbei, geht vorbei. Es gibt einige weitere M-Zeiten für N Überprüfungen, und erst dann gelangt der Code im Browser zum Benutzer. Das ist aber unser Ziel. Unser Ziel ist es, Code zu schreiben, der dem Benutzer wirklich zur Verfügung steht und wirklich zu seinem Vorteil funktioniert.

Heute werden wir über den ersten Teil sprechen. Über das, was vorher passiert, aber nicht wirklich über die Tests.



Übrigens sieht es so aus. Ich war im Tracker an der Reihe, sammelte meine eigenen und zählte den Median. Es stellt sich heraus, dass meine Tickets viel weniger in der Entwicklung sind als beim Überprüfen. Und wie Sie wissen, ist die Wahrscheinlichkeit, dass entweder goto am Anfang oder goto am Ende erscheint, umso höher, je länger es in Schach gehalten wird - und das möchte ich überhaupt nicht.

Und wenn Sie darauf achten, finden Sie hier zwei Wörter auf der Folie: Entwicklung (das tun wir, Entwickler) und Verifikation (das machen wir, aber auch Tester). Daher ist das Problem tatsächlich für Tester relevant.



Das Ziel ist ziemlich prosaisch. Generell möchte ich sagen, dass das Leben vereinfacht werden muss: Wir arbeiten bereits viel mit Ihnen zusammen. Das Ziel sieht ungefähr so ​​aus, aber Sie müssen zugeben, dass es eher kurzlebig ist. Lassen Sie uns einige grundlegende Kriterien hervorheben, von denen das Ziel abhängen kann.

Je weniger Code, desto einfacher ist es für uns. Je schneller wir CI-Prüfungen haben, desto eher werden wir erkennen, ob wir Recht haben oder nicht. Das heißt, lokal kann es im Allgemeinen für immer beginnen. Überprüfungsgeschwindigkeit - Dies gilt direkt für den Tester. Wenn unsere Anwendung groß ist und vollständig überprüft werden muss, ist dies eine enorme Zeitspanne. Die Release-Geschwindigkeit hängt davon ab. Es ist unmöglich, es freizugeben, bis wir alle Prüfungen bestanden haben und bis wir verstehen, dass der Code genau der ist, den wir wollen.

Um einige der Probleme zu lösen, über die wir sprechen, analysieren wir den Abhängigkeitsgraphen von Modulen in unserer Programmiersprache. Und eigentlich wollen wir es beschreiben.



Das Diagramm ist ausgerichtet: Es hat Kanten mit Richtungen. In den Knoten des Diagramms haben wir nur die Module der Sprache, über die wir sprechen. Rippen sind eine bestimmte Art von Bindung. Es gibt verschiedene Arten der Kommunikation.



Schauen wir uns ein alltägliches Beispiel an. Es gibt Datei A. Hier wird etwas aus Datei B importiert, und dies ist eine solche Beziehung zwischen Knoten.



Das gleiche passiert, wenn Sie den Import durch erfordern ersetzen. Tatsächlich ist hier nicht alles so einfach.



Ich schlage vor, da es sich um die Art der Abhängigkeit handelt, sollten Sie mindestens zwei Typen in Betracht ziehen - um Ihre Pipeline zu beschleunigen, um das Durchlaufen von Graphen zu beschleunigen. Es ist notwendig, nicht nur das abhängige Modul, sondern auch das abhängige zu beobachten. Ich schlage vor, Modul A - das Elternteil, B - das Kind aufzurufen, und ich rate Ihnen, die Links immer als doppelt verknüpfte Liste beizubehalten. Dies wird Ihr Leben vereinfachen, ich informiere Sie im Voraus.

Nachdem wir das Diagramm irgendwie beschrieben haben, wollen wir uns darauf einigen, wie wir es erstellen werden.



Es gibt zwei Möglichkeiten. Entweder Ihr Lieblingswerkzeug in Ihrer bevorzugten Programmiersprache mit demselben AST (abstrakte Syntaxbäume) oder Stammgäste. Was ist der Gewinn hier? Die Tatsache, dass Sie hier an niemanden gebunden sind, aber gleichzeitig alles selbst umsetzen müssen. Sie müssen alle Arten von Verbindungen all der Dinge und Technologien beschreiben, die Sie verwenden, ob es sich um einen separaten CSS-Kollektor handelt oder so etwas. Aber Sie haben sozusagen völlige Flugfreiheit.

Darüber hinaus, die zweite Option, werde ich es auch ein wenig fördern, dies ist eine Option nur für die meisten Leute, die bereits ein Build-System konfiguriert haben. Tatsache ist, dass das Montagesystem standardmäßig ein Diagramm erfasst, das von der Konstruktion abhängt.



Schauen wir uns eines der beliebtesten Montagesysteme in Yandex an, das ist Webpack. Hier habe ich ein Beispiel gegeben, wie Sie das gesamte Ergebnis des Webpacks in einer separaten Datei sammeln können, die dann unserem oder einem anderen Analysegerät zugeführt werden kann. Er sammelt es mit Hilfe von AST, die Eichelbibliothek wird benutzt. Sie haben sie vielleicht bemerkt, als etwas gefallen ist. Ich habe es bemerkt.

Und was sind die Vorteile. Tatsache ist, dass Sie bei der Beschreibung Ihres Build-Systems absolut ehrlich nach dem Eintrag gefragt haben. Dies sind die Dateien, aus denen Ihre Abhängigkeiten abgewickelt werden, die anfänglichen Umgehungspunkte. Das ist gut, weil Sie sie nicht erneut aufnehmen müssen. Darüber hinaus ist Webpack und Babel und all dies und Eichel, einschließlich, es ist immer noch nicht Ihre Pflege. Und deshalb werden alle möglichen neuen Funktionen der Sprache, alle möglichen Fehler und alles andere schneller korrigiert als wenn Sie es getan hätten, insbesondere wenn Sie kein sehr großes Team haben. Ja, auch wenn es groß ist, ist es nicht so groß wie Open Source.

Dies ist in der Tat sowohl ein Plus als auch ein Minus. Es ist so, als würde eine solche Doppelkante (zweischneidiges Schwert) erhalten. Tatsache ist, dass dieses Diagramm während der Montage erstellt wird. Es ist gut, das heißt, wir können das Projekt zusammenstellen und das Montageergebnis sofort wiederverwenden. Aber was ist, wenn wir kein Projekt zusammenstellen möchten, sondern nur dieses Diagramm erhalten möchten?

Und so ein großes Minus in der Tat. Wenn Sie benutzerdefinierte Dinge verbunden haben, werden wir viel später über Verbindungen sprechen. Das Build-System lässt Sie dies nicht zu. Oder Sie müssen dies integrieren, z. B. Ihr Webpack-Plugin.



Betrachten Sie ein bestimmtes Beispiel. Ich habe einen Befehl für meine Projektion ausgeführt, in der nur drei Dateien vorhanden sind, und diese Ausgabe erhalten. Und das zeige ich nur einen Schlüssel, der Module heißt. Wir sprechen gerade mit Ihnen über das Abhängigkeitsdiagramm von Modulen, also betrachten wir Module, alles ist logisch.



Ziemlich viele Informationen, aber wir brauchen nicht alles. Lassen Sie einige Punkte und lassen Sie uns darüber sprechen. Angenommen, wir betrachten das erste Modul. Er hat einen Namen, es gibt Gründe. Die Gründe sind nur die Verbindung mit "abhängigen" Modulen, es stellt sich heraus, dass diejenigen, die dieses Modul in sich selbst importieren. Dies sind die Basisdaten, um ein Diagramm darauf zu erstellen.



Beachten Sie außerdem die verwendeten Exporte und bereitgestellten Exporte. Wir werden etwas später darüber sprechen. Das sind aber auch sehr wichtige Dinge.





Und wenn Sie Ihre Entscheidung beschreiben, müssen Sie über die Arten von Verbindungen sprechen, die zwischen Modulen auftreten. Das heißt, wir haben natürlich unser Modulsystem in unserer Sprache: ob es sich um cjs-Module oder esm-Module handelt. Darüber hinaus müssen Sie zustimmen, dass möglicherweise eine Verbindung zwischen Dateien im Dateisystem auf der Ebene des Dateisystems selbst besteht. Dies sind eine Art Framework: Je nachdem, wie Daddies sind, wird es eine Art Framework geben.



Und solch ein banales Beispiel - wenn Sie die Serverseite von Node geschrieben haben, dann konnten Sie ziemlich oft ein so beliebtes npm-Paket wie Config sehen. Damit können Sie Ihre Konfigurationen ganz bequem definieren.



Um es zu verwenden, müssen Sie den Konfigurationsordner abrufen, in dem Sie NODE_PATH haben, und mehrere JavaScript-Dateien angeben, um dort die Konfiguration für verschiedene Umgebungen zu präsentieren. Als Beispiel habe ich einen Papa erstellt, Standard, Entwicklung und Produktion angegeben.



Und tatsächlich funktioniert die gesamte Konfiguration ungefähr so. Das heißt, wenn Sie require ('config') schreiben, liest es nur das Modul in sich selbst und übernimmt den Modulnamen aus der Umgebungsvariablen. Wie Sie verstehen, war dort nicht klar, dass diese Dateien irgendwie verwendet werden, da es keinen direkten Import / Bedarf gibt, würde Webpack es nicht einmal erkennen.


Link von der Folie

Heute haben wir auch über Dependency Injection gesprochen. Ich war nicht inspiriert, aber zur Unterstützung habe ich mir eine der Bibliotheken hier angesehen. Es heißt inversify JS. Wie Sie sehen können, bietet es eine ziemlich benutzerdefinierte Syntax: lazyInject, nameProvider und hier ist es. Und Sie müssen zugeben, es ist nicht klar, um welche Art von Anbieter es sich handelt, welche Art von Modul es hier wirklich injiziert. Und wir brauchen es und wir müssen es verstehen. Das heißt, das Build-System wird wiederum nicht gelöst, und wir müssen es selbst tun.

Angenommen, wir haben ein Diagramm erstellt, und ich schlage vor, Sie speichern es zunächst irgendwo. Was wird uns das ermöglichen? Auf diese Weise können wir eine Art heuristische Analyse durchführen, ein wenig Data Science spielen und uns dabei auf eine Zeitscheibe konzentrieren.



Was ist die Idee? Hier sind in der Tat direkt unsere Daten. Wir haben kürzlich unser Design-System in Yandex.Market implementiert und insbesondere eine Komponentenbibliothek als Teil dieses Design-Systems implementiert. Und hier sehen Sie: Wir betrachten die Anzahl der Importe, die Reaktionskomponente aus unserer Bibliothek, die gemeinsame Komponente. Und Sie können in Verzeichnissen verteilen. In diesem Fall haben wir ein solches Nicht-Mono-Repository und daher plattform.desktop, platform.touch und src.

Was können wir denken, wenn wir diese Zahlen sehen? Wir können die Hypothese aufstellen, dass der Berührungsbefehl die Verwendung gemeinsamer Komponenten nicht zu erhöhen scheint. Dies bedeutet, dass entweder die Komponenten für Mobilgeräte schlecht sind - schlecht gemacht oder der Touch-Befehl faul ist. Aber ist das wirklich so?



Wenn wir in einem längeren Zeitraum, in einem längeren Zeitraum, schauen, können wir nach jeder Veröffentlichung nur Grafiken speichern. Dann werden wir verstehen, dass tatsächlich alles für Berührungen in Ordnung ist und der Indikator darin wächst. Für src ist es sogar noch besser, für Desktop stellt sich heraus, dass dies nicht der Fall ist.



Es gab immer noch eine Frage des Publikums, wie man Managern die Wichtigkeit erklären könnte. Hier ist die Gesamtzahl der Bibliotheksimporte, auch nach Zeit. Welche Manager mögen keine Grafiken? Sie können einen solchen Zeitplan erstellen und feststellen, dass die Nutzung der Bibliothek zunimmt, was bedeutet, dass dies zumindest eine nützliche Sache ist.

Einer meiner Lieblingsteile. Ich werde es ziemlich kurz behandeln. Dies ist eine Suche nach Fehlern in der Grafik. Heute wollte ich mit Ihnen über zwei Arten von Fehlern sprechen: Dies ist eine zyklische Abhängigkeit von Modulen und einigen nicht verwendeten Modulen, dh ein Problem bei der Beseitigung toten Codes.



Beginnen wir mit der zirkulären Abhängigkeit.



Hier scheint alles ganz einfach zu sein. Sie haben bereits einen gerichteten Graphen, Sie müssen dort nur eine Schleife finden. Ich werde erklären, warum ich darüber spreche. Tatsache ist, dass, bevor ich im Grunde genommen die Serverseite auf Node.js schrieb, und wir im Prinzip kein Webpack / Babel verwendet haben, nichts. Das heißt, sie starteten so wie sie sind. Und es gab Bedarf. Wer erinnert sich, wie sich der Import vom Bedarf unterscheidet? Alles ist richtig. Wenn Sie den Code schlecht geschrieben haben, aber ich habe es wirklich getan, können Sie auf Ihrem Server feststellen, dass Ihr Modul nur dann in einer zyklischen Abhängigkeit ist, wenn eine Anfrage von Benutzern kommt oder ein anderes Ereignis funktioniert. Das ist ein eher globales Problem. Bis zur Laufzeit nicht verstehen. Das heißt, der Import ist viel besser, es wird kein solches Problem geben.



Dann nehmen Sie einfach einen beliebigen Algorithmus. Hier habe ich einen ziemlich einfachen Algorithmus genommen. Wir müssen einen Scheitelpunkt finden, der nur eine Art von Kanten hat - entweder eingehend oder ausgehend. Wenn es einen solchen Scheitelpunkt gibt, löschen wir ihn, entfernen die Kanten und setzen diesen Prozess tatsächlich fort. Wir werden feststellen und beweisen, dass es in diesem Diagramm einen Zyklus mit fünf Zyklen gab.

Stimmen Sie zu, wenn Sie es mit Code betrachten, das heißt, Sie können dort immer noch einen Zyklus von zwei oder drei Längen finden, aber es ist unmöglicher, und in unserem Projekt gab es wirklich einen Zyklus von sieben, aber nicht in der Produktion.



Über nicht verwendete Module. Es gibt auch einen eher trivialen Algorithmus. Wir müssen die verbundenen Komponenten in unserem Diagramm hervorheben und nur die Komponenten suchen, die keinen der Eingangsknoten enthalten. In diesem Fall ist dies diese Komponente der Verbundenheit, beide Eckpunkte, wie sich herausstellt, beide Knoten. Dann heißt entry.js. Unabhängig davon, wie es heißt, bedeutet dies, was Sie in der Konfiguration der Eintragsassembly beschrieben haben.



Es gibt aber noch einen anderen Ansatz. Wenn Sie das Diagramm noch nicht gesammelt haben und nur ein Build-System haben, wie ist es dann am billigsten? Markieren wir einfach alle Dateien, die während der Montage in die Assembly gelangt sind. Kennzeichnen Sie sie und erstellen Sie viele. Danach sollten wir viele der Dateien, die Sie im Projekt haben, abrufen und sie einfach subtrahieren. Das ist eine sehr einfache Operation.



Und jetzt sage ich Ihnen nicht nur etwas Theoretisches, ich war inspiriert, kam zu meinem Projekt und tat dies. Und Aufmerksamkeit! Ich habe nicht einmal node_modules gelöscht. Dies habe ich als Wachstumspunkt für die nächste Überprüfung hinterlassen. Kurz gesagt, ich war so von mir selbst inspiriert, dass ich mich entschied, diese Folie irgendwie zu machen und neu zu arrangieren. Lass es so aussehen, denn es ist wirklich cool!

Gute Zahlen, können Sie sich vorstellen, wie alles gut geworden ist? Und dann wurde ich in eine solche Steppe getrieben, dass ich mich wie ein Designer fühlte und dachte, dass dies eine Leistung ist, die ich dem Rahmen hinzufügen möchte. Und wie Sie wissen, stand ich auf, schaute und erkannte, dass ich eher kein Designer, sondern ein Webentwickler war. Aber ich bin kein Dummkopf. Ich habe diesen Rahmen genommen und meiner Website für SEO-Amulette hinzugefügt.



Sie können verwenden, auch der Link ist. Und damit Sie nicht glauben, dass ich Sie täusche - wir sind heute offen -, habe ich mir die Bewertungen wirklich angesehen. Ich denke du kannst ihnen glauben.



Um ehrlich zu sein, sah es ungefähr so ​​aus. Ich habe eine neue Hypobibliothek thanos-js gesehen, sie genommen und eine Poolanfrage erstellt. Im Geheimen habe ich Administratorrechte in unserem Repository. Und ich nahm und verwirrte den Meister. Wie gefällt dir das? Nun, Sie und ich sind ehrlich, und tatsächlich sah alles so aus. Wenn jemand es nicht weiß, ist thanos-js eine Bibliothek, die einfach 50% Ihres Codes zufällig entfernt.



Eigentlich habe ich die Bibliothek dort sowieso benutzt, aber die Bibliothek heißt anders. Es wird als Diffektor bezeichnet, und jetzt werden wir mit Ihnen darüber sprechen. Und hier möchte ich darauf hinweisen, dass die Poolanforderung mit 44.000 Codezeilen ziemlich bedeutend ist, und Sie können sich vorstellen, dass sie den Test zum ersten Mal bestanden hat. Das heißt, worüber ich spreche, kann wirklich funktionieren.



Diffektor Tatsächlich beschäftigt er sich nicht nur mit der Entfernung nicht verwendeter Module, der Suche nach Fehlern im Diagramm, sondern auch mit einer wichtigeren Aufgabe. Was ich ursprünglich erklärt habe, war, dem Entwickler und Tester zu helfen, jetzt werden wir darüber sprechen. Und so etwas funktioniert es.

Wir erhalten eine Liste der geänderten Dateien mit dem Versionskontrollsystem. Wir haben bereits einen Graphen erstellt - Diffector erstellt ihn. Und für jede solche geänderte Datei suchen wir nach dem Pfad zum Eintrag und markieren den geänderten Eintrag. Der Eintrag entspricht den Anwendungsseiten, die der Benutzer sehen wird. Das ist aber ziemlich logisch.

Und was gibt uns das? Zum Testen - wir wissen, welche Seiten in der Anwendung geändert wurden. Wir können dem Tester sagen, dass nur sie einen Test wert sind. Wir können unserem ci-job, der Autotests ausführt, auch mitteilen, dass nur diese Seiten einen Test wert sind. Und für Entwickler ist alles viel einfacher, denn jetzt schreiben Tester nicht an Sie und fragen nicht: "Warum müssen Sie testen?"



Schauen wir uns ein Beispiel für die Funktionsweise des Diffektors an. Hier haben wir ein bestimmtes Verzeichnis, pages.desktop / *. Es enthält nur eine Liste der Seiten selbst. Und die Seiten werden auch durch mehrere Dateien beschrieben. Der Controller ist die Serverseite der Seite. Ansicht ist eine Art Reaktionsteil. Und deps, das ist von einem anderen Build-System. Wir haben nicht nur Webpack, sondern auch ENB.



Und ich habe einige Änderungen am Projekt vorgenommen, an einer leeren Datei, deren Struktur Sie gesehen haben. Das gibt mir der Diffektor. Ich habe es gerade gestartet, Diffector ist eine Befehlszeilenanwendung. Ich habe es gestartet, er sagt mir, dass ich eine Seite geändert habe, die BindBonusPage heißt.



Ich kann es auch im ausführlichen Modus ausführen, einen detaillierteren Bericht anzeigen und wirklich sehen, dass es zumindest in einem so einfachen Fall funktioniert. Wie wir sehen, haben sich in unserer BindBonusPage die Indexdatei und der Controller geändert.

Aber mal sehen, was passiert, wenn wir etwas anderes ändern.



Ich habe etwas anderes geändert. Und der Diffektor sagte mir, dass ich neun Seiten gewechselt habe. Und das macht mich nicht mehr glücklich, als würde er mir nicht wirklich helfen.



Mal sehen warum? Es werden nun die Gründe angezeigt, warum diese Seite als geändert angesehen wurde. Und wie wir sehen, das gleiche hier. - uikit.



diff. . , diffector . , - , .



, , . , , entry, , , test-scope, . .

. , , , , .



. - , . — i18n, , . , , , . , , - .

? , , , , , .



- . , B , , -2 . . , esm.



.



.



, value, . , , . , .

, AST, , 250 , , . , , - , , .



, - GlobalContext - , . , modify, , ? , - GlobalContext. . . , side effects. , , webpack, , . , webpack sideEffects: true, . .



, - - . , . diffector, . , , . — , . , .

, , diff, expand, log, , , , .



, . D, diff. , , , . , , . , , . .

, , . . . , — , . . . , , , , , . . , .

— diffector ? , , , .



- , , .



, , entry. entry. diffector.



. -. , .



, entry -, .



. diffector. . , , - , -. , . : , BindBonusPage, -, . . , , - . .





— CI. . : , , .



, . 43 — testing, , .



. , , .



, . : , , . , , , , , . , , - .



Ich fasse zusammen. , , , . - , - , , , . .

, , , output . , . — . — . Vielen Dank!

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


All Articles