Unity Dashboard - Lehren aus der Skalierung unserer Frontends, Entwicklungskultur und Prozesse

Bei Unity haben wir uns kürzlich vorgenommen, unsere Dashboards zu verbessern - ein Unterfangen, das nicht nur unseren Frontend-Tech-Stack, sondern auch unsere Arbeitsweise und Zusammenarbeit dramatisch verändert hat.

Wir haben Best Practices und Tools entwickelt, mit denen wir unsere Frontend-Architektur skalieren, Produkte mit hervorragender UX und Leistung erstellen und neue Funktionen schneller bereitstellen können.

Dieser Artikel fasst diese Praktiken zusammen und zielt darauf ab, so viele Gründe wie möglich für jede Entscheidung zu liefern. Aber zuerst ein Zusammenhang.

Das Vermächtnis

In Bezug auf die Anzahl der Ingenieure hat Unity seine Mitarbeiterzahl in den letzten 4 Jahren mehr als vervierfacht. Da das Unternehmen sowohl organisch als auch durch Akquisitionen wuchs, wuchs auch sein Produktangebot. Während die ursprünglich bei Unity entwickelten Produkte in Bezug auf Technik und Designsprache weitgehend einheitlich waren, waren es die neu erworbenen natürlich nicht.

Infolgedessen hatten wir mehrere visuell unterschiedliche Dashboards, die unterschiedlich funktionierten und sich benahmen und keine gemeinsamen Navigationselemente gemeinsam hatten. Dies führte zu einer schlechten Benutzererfahrung und frustrierten Benutzern. Im wahrsten Sinne des Wortes kostete uns der Zustand der Frontends unserer Produkte Umsatz.

Nach der Analyse des Portfolios unserer Produkte haben wir drei verschiedene Abschnitte ermittelt, in die Unity Dashboard unterteilt ist: Entwickeln, Betreiben und Erfassen, die jeweils unterschiedliche Geschäftsanforderungen erfüllen und für unterschiedliche Kundengruppen bestimmt sind und somit weitgehend voneinander unabhängige Funktionssätze enthalten .

Diese neue Struktur und die Einführung gemeinsamer Navigationselemente zielten darauf ab, das erste große Problem zu lösen, mit dem unsere Benutzer konfrontiert waren - wo sie die gesuchten Informationen und Konfigurationsoptionen finden konnten und auf dem Papier alles gut aussah, wie es weiterging dorthin zu gelangen waren alles andere als offensichtlich.

Überlegungen

Viele unserer Entwickler waren sehr aufgeregt über die Möglichkeit, zu React und seinem moderneren Tech-Stack zu wechseln. Da diese Lösungen in großen Anwendungen kampferprobt worden waren und ihre Best Practices und Konventionen größtenteils ausgebügelt waren, sahen die Dinge sehr vielversprechend aus.

Was unsere Entwickler jedoch am besten wussten und in was die meisten unserer aktiv entwickelten Anwendungen geschrieben wurden, war AngularJS. Die Entscheidung, alles auf einmal zu migrieren, wäre eine Katastrophe gewesen, die darauf gewartet hätte, geschehen zu können. Stattdessen haben wir uns vorgenommen, unsere Annahmen zunächst in einem viel kleineren Maßstab zu testen.

Die vielleicht unzusammenhängendste Gruppe von Produkten, die wir je hatten, waren die Monetarisierungs-Dashboards . Diese Projekte, die schließlich unter dem Dach des Operate-Dashboards landen würden , waren in fast jeder Hinsicht sehr unterschiedlich: verwendete Technologien, Ansatz für UI / UX, Entwicklungspraktiken, Codierungskonventionen - wie Sie es nennen.

So sah die Situation ungefähr aus:

Nach einigem Brainstorming haben wir die Hauptbereiche identifiziert, an denen wir arbeiten müssen, um alle Produkte zusammenzubringen:

1. Ein einzelnes Produkt

Wir brauchten diese Dashboards (aufgeteilt auf mehrere Anwendungen, Domänen und Tech-Stacks), um:

  • Fühlen Sie sich wie ein einzelnes Produkt (keine vollständigen Seitenumleitungen, wenn der Benutzer durch die Seiten aller verschiedenen Anwendungen navigiert)
  • Haben Sie ein einheitliches Erscheinungsbild
  • Allgemeine Navigationselemente einschließen sind immer sichtbar und sehen gleich aus, unabhängig davon, welchen Teil des Dashboards der Benutzer besucht

2. Legacy-Unterstützung

Während wir bei der Auswahl der Technologie für unsere neue Frontend-Lösung einen sauberen Plan hatten, mussten wir uns auf die Legacy-Projekte einstellen, die in das neue System integriert werden mussten. Eine Lösung, die keinen großen Umbauaufwand beinhaltete und die die Entwicklung von Features nicht stoppen oder monatelang schleppen würde, ohne dass ein Ende in Sicht wäre.

3. Praktiken und Werkzeuge

Während fast alle Teams AngularJS verwendeten, wurden verschiedene Tools verwendet, um die gleichen Herausforderungen zu bewältigen. Verschiedene Testläufer und Assertionsbibliotheken, Statusverwaltungslösungen oder deren Fehlen, jQuery vs. native Browser-Selektoren, SASS vs LESS, Diagrammbibliotheken usw.

4. Entwicklerproduktivität

Da jedes Team seine eigene Lösung zum Entwickeln, Testen und Erstellen seiner Anwendung hatte, war die Entwicklungsumgebung häufig mit Fehlern, manuellen Schritten und Ineffizienzen behaftet.

Darüber hinaus arbeiten viele unserer Teams an Standorten, die durch einen Unterschied von 10 Stunden voneinander getrennt sind (Helsinki, Finnland und San Francisco), was eine effiziente Entscheidungsfindung für gemeinsame Teile zu einer echten Herausforderung macht.

Das neue

Unsere Hauptschwerpunkte waren:

  1. Ermutigen und bewahren Sie agile Arbeitsweisen in unseren Teams und lassen Sie die Teams weitgehend unabhängig voneinander sein
  2. Nutzen und entwickeln Sie so viele gängige Tools und Konventionen wie möglich, um sie zu dokumentieren und leicht zugänglich und benutzerfreundlich zu machen

Wir waren davon überzeugt, dass das Erreichen dieser Ziele unsere Markteinführungszeit und die Produktivität der Entwickler erheblich verbessern würde. Dazu benötigten wir eine Lösung, die:

  • Erstellen Sie Produktfunktionen mit besserer Benutzererfahrung
  • Verbessern Sie die Codequalität
  • Ermöglichen Sie eine bessere Zusammenarbeit, ohne den Arbeitsfortschritt anderer zu blockieren.

Wir wollten auch die Umstellung auf einen modernen Tech-Stack fördern und vereinfachen, um unsere Entwickler mit ihrer Arbeit zufriedener zu machen und uns im Laufe der Zeit von unseren veralteten Frameworks und Tools zu entfernen.

Das sich ständig weiterentwickelnde Ergebnis unserer Arbeit ist ein reaktionsbasiertes SPA, das in einem Monorepository integriert ist, in dem alle Seiten und größeren Funktionen in weitgehend unabhängige Code-Bundles integriert werden, die bei Bedarf geladen werden und von mehreren Teams gleichzeitig entwickelt und bereitgestellt werden können .

Um alle Legacy-Anwendungen zu sandboxen und sie dennoch im Kontext derselben neuen Anwendung anzuzeigen, laden wir sie in einen Iframe, von dem aus sie über einen über die postMessage()API implementierten Nachrichtenbus mit dem Haupt-SPA kommunizieren können .

Das Monorepository

Hier ist die Verzeichnisstruktur, mit der wir begonnen haben:

/src /components /scenes /foo /components package.json foo.js /bar /components package.json bar.js package.json index.js

Das package.jsonVerzeichnis im Stammverzeichnis enthält eine Reihe von devDependencies Verantwortlichkeiten für die Entwicklung, den Test und die Build-Umgebung der gesamten Anwendung, enthält jedoch auch dependenciesden Kern der Anwendung (dazu etwas später).

Alle größeren UI-Blöcke werden als Szenen bezeichnet . Jede Szene enthält ein , package.jsonwo dependenciesdurch die Szene der Komponenten verwendet werden definiert. Dies macht zwei Dinge möglich:

  1. Die Bereitstellung aktualisiert nur die Dateien, die geändert wurden

    Der Erstellungsschritt kompiliert separate Hersteller- und App-Bundles für jede Szene und benennt sie mit einem Hash, der sich nur ändert, wenn sich der Inhalt der Datei geändert hat. Dies bedeutet, dass unsere Benutzer nur Dateien herunterladen, die sich seit ihrem letzten Besuch geändert haben, und nicht mehr.

  2. Szenen werden nur bei Bedarf geladen

    Wir laden alle Szenen asynchron und nach Bedarf, was die Ladezeiten der gesamten Anwendung drastisch verbessert. Das "On Demand" bedeutet hier normalerweise, eine bestimmte Route zu besuchen oder eine UI-Aktion auszuführen, die einen dynamischen Modulimport ausführt.

So sieht ein solches Setup in der Praxis aus (zur besseren Lesbarkeit vereinfacht):

// In src/routes.jsconst FooLoader = AsyncLoadComponent( () => import(‘src/scenes/foo/foo’), GenericPagePreloader,);
// In src/scenes/foo/foo.js 

Das AsyncLoadComponentist ein dünner Wrapper React.lazy(), der zusätzlich eine Preloader-Komponente akzeptiert, die durch den Fallback übergeben wurde React.Suspense(), und eine Verzögerung, nach der der Preloader gerendert werden sollte, wenn die Szene noch nicht vollständig geladen wurde.

Dies ist nützlich, wenn Sie sicherstellen möchten, dass unsere Benutzer vom Zeitpunkt der Anforderung einer Szene bis zum Zeitpunkt des Herunterladens aller Dateien, des Abschlusses aller kritischen API-Anforderungen und der Komponente denselben Preloader ohne Unterbrechung oder Flash des Inhalts sehen hat das Rendern beendet.

Komponentenebenen

Wenn jede Anwendung wächst, entwickeln sich ihre Verzeichnisstruktur und Abstraktionen mit. Nach ungefähr einem halben Jahr des Erstellens und Verschiebens von Features in die neue Codebasis erwies sich ein einziges Komponentenverzeichnis als unzureichend.

Wir brauchten unsere Verzeichnisstruktur, um uns zu informieren über:

  • Wurden die Komponenten generisch entwickelt oder sind sie nur für einen bestimmten Anwendungsfall gedacht?
  • Sind sie generisch genug, um in der gesamten Anwendung verwendet zu werden, oder sollten sie nur in bestimmten Kontexten verwendet werden?
  • Wer ist für den Code verantwortlich und kennt ihn am besten?

Basierend darauf haben wir die folgenden Komponentenebenen definiert :

1. Anwendungsspezifisch (src / app)

Einwegkomponenten, die für bestimmte Anwendungsfälle in dieser Anwendung geeignet sind und nicht zur Wiederverwendung oder Extraktion in die Komponentenbibliothek (Routen, Fußzeile, Seitenkopf usw.) bestimmt sind.

2. Generisch (src / Komponenten)

Generische Mehrzweckkomponenten für die gesamte Anwendung und ihre Szenen. Sobald wir eine stabile API für diese Komponenten gefunden haben, können sie in die allgemeine Komponentenbibliothek verschoben werden (mehr dazu weiter unten).

3. Komponenten einer einzelnen Szene (src / scene / my-scene / components)

Komponenten, die für einen bestimmten Anwendungsfall entwickelt wurden; nicht für andere Szenen gedacht. In Fällen, in denen eine Komponente aus einer Szene in einer anderen verwendet werden muss, verwenden wir:

4. Komponenten mit mehreren Szenen (src / scene / components / my-feature)

Komponenten, die in mehreren Szenen verwendet werden, aber nicht generisch genug sind, um an anderer Stelle verwendet zu werden. Um zu veranschaulichen, warum src/componentses nicht gut genug ist, sie einfach zu verschieben :

Stellen Sie sich vor, Sie hatten bisher eine einzige Szene, die Komponenten enthielt, mit denen einige ziemlich spezifische Datendiagramme erstellt wurden. Ihr Team erstellt jetzt eine zweite Szene, in der unterschiedliche Daten für die Diagramme verwendet werden. Optisch sehen die beiden jedoch ziemlich gleich aus.

Das Importieren von Komponenten aus einer Szene in eine andere würde die Kapselung der Szene unterbrechen und bedeuten, dass wir nicht mehr sicher sein können, ob Änderungen an den Komponenten einer einzelnen Szene nur diese eine Szene betreffen.

Zu diesem Zweck wird jede Komponente oder Gruppe von Komponenten, die grob als Feature bezeichnet wird, dort platziert, src/scenes/componentswo sie importiert und von jedem anderen Team verwendet werden kann:

Wenn ein Team Szenenkomponenten verwenden möchte, die von einem anderen Team entwickelt wurden, sollten Sie sich zunächst an dieses Team wenden, um herauszufinden, ob der Anwendungsfall, für den Sie diese Komponenten beabsichtigen, in Zukunft sicher unterstützt werden kann. Wenn Sie dem Team, das den Code ursprünglich entwickelt hat, einen Hinweis geben, wird verhindert, dass in Zukunft fehlerhafte Funktionen ausgeliefert werden, wenn der von Ihnen verwendete Code unweigerlich auf eine Weise geändert wird, die Sie nicht erwartet haben (natürlich, wie könnten Sie!) die möglicherweise nicht immer von den Unit-Tests erfasst werden.

5. Gemeinsame Bibliothek

Komponenten, die wir in der Produktion getestet haben und die wir in unsere gemeinsam genutzte Komponentenbibliothek extrahieren möchten, die von anderen Dashboard-Teams bei Unity verwendet wird.

Ode an gemeinsame Abhängigkeiten

Während es sehr praktisch wäre, jeden Teil unserer Anwendung in einer vollständig isolierten Umgebung erstellen und bereitstellen zu können, werden bestimmte Abhängigkeiten - sowohl externe Bibliotheken als auch interner Anwendungscode - einfach in der gesamten Codebasis verwendet. Dinge wie React selbst, Redux und alle Redux-bezogenen Logik, gemeinsame Navigationskomponenten usw.

Änderungen ausrollen

Im Moment ist es nicht praktisch und in vielen Fällen einfach unmöglich, die Szenen vollständig zu kapseln. Es würde entweder erforderlich sein, viele Abhängigkeiten mehrmals zu versenden und dabei das Laden von Seiten zu verlangsamen, oder Abstraktionen zu erstellen, damit bestimmte Bibliotheken auf eine Weise funktionieren, für die sie nicht entwickelt wurden.

Während sich die Webentwicklung und ihr Ökosystem weiterentwickeln, scheinen die Bibliotheken immer eigenständiger und gekapselter zu werden, was hoffentlich in Zukunft wenig bis gar keine gemeinsamen Abhängigkeiten und eine echte Isolation zwischen allen Modulen bedeuten wird.

Der vielleicht größte Nachteil beim Erstellen umfangreicher Anwendungen besteht darin, Codeänderungen und Abhängigkeitsaktualisierungen durchzuführen, ohne dabei etwas zu beschädigen

Die Verwendung eines Monorepositorys ermöglicht es (wenn auch nicht obligatorisch), Änderungen und Aktualisierungen des Codes schrittweise und sicherer durchzuführen. Wenn eine Änderung Probleme verursacht, betreffen diese Probleme nur einen kleinen Teil der Anwendung, nicht das gesamte System.

Und während für einige die Möglichkeit, Aktualisierungen für mehrere nicht verwandte Bereiche der Codebasis gleichzeitig durchzuführen, von Vorteil wäre, bedeutet dies, dass mehrere Teams an derselben Codebasis arbeiten und nicht alle Funktionen der anderen Teams genau kennen Beim Aufbau des Anwendungsgerüsts und beim Ergreifen von Maßnahmen zur Minimierung des Bruchrisikos ist große Vorsicht geboten.

Wie vermeide ich es, Dinge zu zerbrechen?

Die vielleicht grundlegendste Strategie, die uns dabei hilft, ist neben der Isolation von Szenen eine hohe Abdeckung durch Unit-Tests .

  1. Testen

Die Komponententests sind natürlich nicht alles - viele ausgereifte Produkte in moderatem Maßstab investieren schließlich in Integrationssuiten und e2e-Tests, mit denen sich besser überprüfen lässt, ob die Anwendung insgesamt wie erwartet funktioniert. Mit zunehmender Anzahl von Funktionen steigen jedoch auch die Wartungskosten und die Zeit, die für deren Ausführung erforderlich sind - Kosten, die für weniger wichtige, aber dennoch wichtige Funktionen nicht immer gerechtfertigt sind.

Einige Lektionen, die wir aus verschiedenen Teststrategien gelernt haben:

  • Versuchen Sie, so viel Code wie möglich zu testen, insbesondere: bedingte Logik, Datentransformationen und Funktionsaufrufe
  • Investieren Sie in Integrationstests und nutzen Sie sie in vollem Umfang, bevor Sie sich entscheiden, e2e-Tests zu schreiben. Die anfänglichen Kosten für Integrationstests sind viel höher, verblassen jedoch im Vergleich zum Wartungspreis einer e2e-Suite
  • Versuchen Sie, nicht zu überreagieren, indem Sie mit dem Schreiben von e2e-Tests für Dinge beginnen, die nicht durch Unit- oder Integrationstests erfasst wurden. Manchmal sind die Prozesse oder Werkzeuge fehlerhaft
  • Lassen Sie Testfälle das Verhalten der Benutzeroberfläche und nicht die Implementierungsdetails erklären
  • Automatisierte Tests können manuelle Tests nicht vollständig ersetzen

2. Minimieren Sie die Oberfläche des gemeinsam genutzten Codes

Abgesehen vom Testen wird der in der gesamten Anwendung wiederverwendete Code auf ein angemessenes Minimum beschränkt. Eine der bisher nützlichsten Strategien bestand darin, die am häufigsten verwendeten Komponenten und den Code in eine gemeinsam genutzte Komponentenbibliothek zu verschieben, von wo aus sie als Abhängigkeiten in Szenen verwendet werden, in denen sie benötigt werden. Auf diese Weise können wir die meisten Änderungen schrittweise pro Team oder Seite einführen.

3. Rechenschaftspflicht

Last but not least ist es ein großer Faktor für die Zusammenarbeit mehrerer Teams innerhalb derselben Codebasis, wenn Entwickler dazu ermutigt werden, die persönliche Verantwortung und Verantwortung für das Produkt zu übernehmen , anstatt die Verantwortung für die ordnungsgemäße Prüfung, ob alles funktioniert, auf Qualitätssicherung, Tester oder Mitarbeiter zu übertragen Automatisierung.

Dies überträgt sich auch auf Codeüberprüfungen. Es ist schwieriger sicherzustellen, dass jede Änderung sorgfältig geprüft wird, als es auf den ersten Blick scheint. Durch die enge Zusammenarbeit des Teams entsteht ein gesundes Maß an Vertrauen zwischen den Mitgliedern. Dieses Vertrauen kann jedoch manchmal dazu führen, dass Menschen weniger sorgfältig mit Änderungen umgehen, die von erfahreneren oder anderweitig vertrauenswürdigen Entwicklern vorgenommen werden.

Um die Sorgfalt zu fördern, betonen wir, dass der Autor der PR und der Prüfer gleichermaßen dafür verantwortlich sind, dass alles funktioniert .

Komponentenbibliothek

Um auf allen Seiten unserer Dashboards das gleiche Erscheinungsbild zu erzielen, haben wir eine Komponentenbibliothek entwickelt. Was in unserem Ansatz steht, ist, dass in dieser Bibliothek fast nie neue Komponenten entwickelt werden.

Jede Komponente wird, nachdem sie in der Codebasis des Dashboards entwickelt wurde, zuerst in einer Reihe von Funktionen in dieser Codebasis verwendet. Normalerweise fühlen wir uns nach einigen Wochen sicherer, dass die Komponente verschoben werden kann, vorausgesetzt:

  • Die API ist flexibel genug, um die vorhersehbaren Anwendungsfälle zu unterstützen
  • Die Komponente wurde in verschiedenen Kontexten getestet
  • Die Leistung, Reaktionsfähigkeit und UX werden berücksichtigt

Dieser Prozess folgt der Dreierregel und soll uns helfen, nur Komponenten freizugeben, die wirklich wiederverwendbar sind und in einer Vielzahl von Kontexten verwendet wurden, bevor sie in unsere gemeinsame Bibliothek verschoben werden.

Einige Beispiele für die Komponenten, über die wir uns bewegen würden, wären: Fußzeile, Seitenkopf, seitliche und obere Navigationselemente, Layoutbausteine, Banner, aktivierte Versionen von Schaltflächen, Typografieelemente usw.

In den Anfangszeiten befand sich die Komponentenbibliothek in derselben Codebasis wie die Anwendung selbst. Wir haben es seitdem in ein separates Repository extrahiert, um den Entwicklungsprozess für andere Teams bei Unity demokratisierter zu gestalten - wichtig, wenn es um seine Einführung geht.

Modulares Komponentendesign

Für die längste Zeit bedeutete der Bau wiederverwendbarer Komponenten die Bewältigung mehrerer Herausforderungen, von denen viele oft keine guten Lösungen hatten:

  • So importieren Sie die Komponente einfach zusammen mit ihren Stilen und nur das
  • So überschreiben Sie Standardstile ohne Selektorspezifitätskriege
  • Bei größeren Komponenten, die aus mehreren kleineren bestehen, wie das Styling der kleineren Komponente überschrieben wird

Unser Dashboard sowie unsere Komponentenbibliothek hängen stark von der Material-Benutzeroberfläche ab und verwenden diese. Was an der Styling-Lösung von Material UI einzigartig überzeugend ist, ist das Potenzial von JSS und seiner Unified Styling Language (lesenswert), das es ermöglicht, UIs zu entwickeln, die wie im Fall von CSS-Modulen vom Design gekapselt sind , und die oben genannten Probleme zu lösen Probleme mit einem Schritt.

Dies unterscheidet sich erheblich von Ansätzen wie BEM, die eine konventionelle Einkapselung bereitstellen , die tendenziell weniger erweiterbar und weniger eingekapselt ist.

Living Styleguide

Eine Komponentenbibliothek wäre nicht vollständig, ohne die darin enthaltenen Komponenten zu präsentieren und die Komponenten zu sehen, wenn sie sich im Laufe der Releases ändern.

Wir haben ziemlich gute Erfahrungen mit Storybook gemacht, das unglaublich einfach einzurichten und zu starten war, aber nach einiger Zeit stellten wir fest, dass eine robustere und durchgängigere Lösung erforderlich war. Ziemlich nah an dem, was Styleguidist bietet, aber mehr auf unsere Bedürfnisse zugeschnitten.

Bestehende Designdokumente

Die Dokumentation, die als Hauptinformationsquelle für die neueste Konstruktionsspezifikation diente, befand sich in Confluence, wo die Konstrukteure für jede Komponente eine aktuelle Spezifikation mit Screenshots führten, in denen zulässige Anwendungsfälle, Zustände und Variationen der Komponente aufgeführt sind Best Practices sowie Details wie Abmessungen, verwendete Farben usw. Nach diesem Ansatz standen wir vor einer Reihe von Herausforderungen:

  • Die Materialdesignspezifikationen entwickeln sich ständig weiter und aus diesem Grund haben wir oft Zeit damit verbracht, alle Screenshots und Richtlinien zu aktualisieren, oder unsere Designrichtlinien veraltet zu lassen
  • Herauszufinden, was korrekter ist: Implementierung oder Spezifikation waren nicht immer einfach. Da wir Storybook-Demos für jede Komponente und für jede Bibliotheksversion veröffentlicht haben, konnten wir sehen, was und wie sich geändert hat. Wir konnten nicht dasselbe für die Designspezifikation tun.
  • Screenshots und Videos können nur so viel kommunizieren . Um Komponenten von hoher Qualität bereitzustellen, die von mehreren Teams verwendet werden können, muss überprüft werden, ob jede Komponente in allen Auflösungen funktioniert, fehlerfrei ist und über eine gute Benutzeroberfläche verfügt. Dies war schwierig, ohne dass der Designer buchstäblich neben Ihnen saß, um die zu sehen Implementierungsdemo wird auf dem Bildschirm angezeigt

Komponentendokumentation App

Unsere Dokumentations-App soll die Möglichkeit einer effizienten Zusammenarbeit zwischen Designern und Ingenieuren bieten, um es beiden Parteien einfacher und weniger zeitaufwändig zu machen, Komponenten zu dokumentieren, zu überprüfen und zu entwickeln. Um genauer zu sein, mussten wir:

  • Haben Sie einen einzigen Bezugspunkt, der die Komponenten zeigt , wiesollten sie aussehen, sich verhalten und verwendet werden - für jede Version vorgesehen - und detaillierte Beschreibungen durch Live-Demos ersetzen
  • Machen Sie es Designern und Entwicklern so einfach, an Komponenten und ihren Dokumenten zusammenzuarbeiten, bevor die Komponenten veröffentlicht werden - ohne dass Sie Videos, Screenshots teilen oder sich physisch am selben Ort befinden müssen
  • Trennen Sie die Entwürfe in das, was wir vorhaben, und in das, was getan wurde

Ähnlich wie zuvor wird bei jeder Veröffentlichung der Komponentenbibliothek eine neue Version des Living Styleguides veröffentlicht. Diesmal gibt es jedoch einige Unterschiede:

  1. Designer tragen direkt zur Komponentendokumentation bei, indem sie Dokumentationsdateien über die Github-Benutzeroberfläche bearbeiten und Änderungen an der neuesten Version vornehmen.
  2. Komponentendemos wie WYSIWYG - Der gleiche Code, den Sie als Beispiel für die Implementierung der Komponente sehen, wird zum Rendern der Demo verwendet, einschließlich aller Importe von Zwischendateien, Variablendeklarationen usw. Als zusätzlichen Bonus werden eingewickelte Komponenten withStyles()korrekt angezeigt (Problem vorhanden) im Storybook im Moment).
  3. Änderungen an den Dokumenten und am Code sind fast sofort sichtbar, ohne dass der Zweig lokal ausgecheckt und die Dokumentations-App gestartet werden muss. Die App wird bei jedem Commit neu erstellt und veröffentlicht.

Entwicklungserfahrung

Eines der Hauptziele von Codeüberprüfungen besteht darin, sicherzustellen, dass jede Änderung sorgfältig geprüft, berücksichtigt und getestet wird, bevor sie zusammengeführt und bereitgestellt wird.

Um diese Aufgabe so frei wie möglich zu gestalten, haben wir einen Vorschau-Server entwickelt , mit dem jedes Mal, wenn eine PR erstellt oder aktualisiert wird, ein neuer Build unserer Anwendung erstellt werden kann.

Unsere Designer, Produktmanager und Ingenieure können jede Änderung testen, bevor sie in Staging- und Produktionsumgebungen und innerhalb von Minuten nach der Änderung zusammengeführt werden.

Schlussworte

Es ist fast ein Jahr her, seit wir uns verpflichtet haben, unsere Dashboards zu konsolidieren. Wir haben diese Zeit damit verbracht zu lernen, wie man ein großes, aber gesundes Softwareprojekt entwickelt, wie man die Zusammenarbeit und Kommunikation verbessert und wie man die Qualitätsgrenze für uns selbst höher legt.

Wir haben ein Frontend-Projekt nicht nur in Bezug auf Codezeilen skaliert, sondern auch in Bezug auf die Anzahl der Ingenieure, die innerhalb seiner Codebasis arbeiten - eine Zahl, die sich von Anfang an vervierfacht hat.

Wir haben den Umgang mit Zeitunterschieden zwischen unseren Teams um 180 Grad geändert und uns von einem Modell, bei dem unsere Teams völlig isoliert arbeiteten, zu einem Modell entwickelt, bei dem enge Zusammenarbeit und Kommunikation alltäglich sind.

Obwohl wir noch einen langen Weg vor uns haben, um sicherzustellen, dass wir unseren Ansatz auf mehr Teams und größere Herausforderungen ausweiten können, haben wir bereits eine Reihe von Verbesserungen festgestellt:

  • Roadmap und Sichtbarkeit der Arbeit

    Da es einen Ort gibt, an dem die gesamte Arbeit stattfindet, wird der Fortschritt verfolgt und alle Probleme werden gesammelt

  • Entwicklungsgeschwindigkeit und Time-to-Market

    Neue Funktionen können größtenteils aus bereits vorhandenen und gut getesteten Komponenten erstellt werden - leicht über unsere Dokumentations-App auffindbar

  • Codequalität und Testabdeckung

    Beim Erstellen neuer Dinge gibt es normalerweise bereits eine Lösung für ein ähnliches Problem, die in Reichweite ist, zusammen mit Beispielen zum Testen

  • Gesamtqualität & UX

    Das Testen von Funktionen und das Sicherstellen ihrer Qualität ist jetzt einfacher als je zuvor, da Designer, Produktmanager und andere Stakeholder jede Änderung auf ihrem eigenen Computer mit ihren eigenen Konten und Datensätzen testen können

Auf dem Weg dorthin sind wir natürlich auf eine Reihe von Herausforderungen gestoßen, die wir lösen müssen oder die wir in Zukunft lösen müssen:

  • Build & CI-Leistung

    Mit zunehmender Anzahl von Abhängigkeiten, Build-Bundles und Tests wächst auch die Zeit, die für eine Bereitstellung benötigt wird. In Zukunft müssen wir Werkzeuge entwickeln, mit denen wir nur die geänderten Teile erstellen, testen und bereitstellen können.

  • Entwicklungskultur

    Um gesunde Software zu entwickeln, müssen wir kontinuierlich an gesunden Wegen der Kommunikation und des Austauschs von Ideen arbeiten, und textbasierte Kommunikation erschwert diese Aufgabe. Wir arbeiten daran, dieses Problem durch eine Reihe regelmäßiger Schulungen für Führungskräfte zu lösen, Open-Source-Arbeitsweisen zu entwickeln und ein paar Treffen pro Jahr zu organisieren, bei denen sich die Teams persönlich treffen können.

  • Bruchisolierung & Updates

    Mit zunehmender Anzahl von Funktionen und Seiten benötigen wir eine robustere Methode zum Isolieren unserer Anwendungsmodule, um zu verhindern, dass sich Schäden ausbreiten, wenn etwas schief geht. Dies kann erreicht werden, indem der gesamte gemeinsam genutzte Code (Redux-Logik, src / components) versioniert wird oder in extremen Fällen eigenständige Builds bestimmter Funktionen erstellt werden.

Geben Sie damals, heute und in Zukunft an

Bei der Migration wurde von AngularJS zu React gewechselt. So hat sich die Situation im letzten Jahr verändert:

Es ist ein Wrap! Danke fürs Lesen! Sie finden mich hier auf LinkedIn.

Wenn es für Sie interessant ist, an ähnlichen Herausforderungen zu arbeiten, suchen wir immer nach talentierten Ingenieuren, die sich unseren Teams auf der ganzen Welt anschließen.