Wie vermeiden wir CI-getriebene Entwicklung?

45

Ich arbeite an einem sehr großen, von der Forschung geleiteten Open-Source-Projekt mit einer Reihe anderer regelmäßiger Mitarbeiter. Da das Projekt jetzt ziemlich groß ist, ist ein Konsortium (bestehend aus zwei Vollzeitmitarbeitern und wenigen Mitgliedern) für die Pflege des Projekts, die kontinuierliche Integration (CI) usw. zuständig. Sie haben einfach keine Zeit für die Integration von externen Mitarbeitern Beiträge aber.

Das Projekt besteht aus einem "Kern" -Framework, etwa einer halben Million Codezeilen, einer Reihe von "Plugins", die vom Konsortium verwaltet werden, und mehreren externen Plugins, von denen die meisten nicht vorhanden sind. nicht einmal bewusst.

Derzeit erstellt unser CI den Core und die gepflegten Plugins.

Eines der großen Probleme, mit denen wir konfrontiert sind, ist, dass die meisten Mitwirkenden (und besonders die gelegentlichen) nicht 90% der gepflegten Plugins erstellen. Sie überprüften, ob der Code auf ihrem Computer kompiliert wurde, bevor sie eine Pull-Anfrage für GitHub stellten.

Der Code funktioniert, sie sind glücklich, und dann ist das CI fertig und die Probleme beginnen: Die Kompilierung ist in einem vom Konsortium gepflegten Plugin fehlgeschlagen, das der Mitwirkende nicht auf seinem Computer erstellt hat.

Dieses Plugin kann Abhängigkeiten zu Bibliotheken von Drittanbietern aufweisen, z. B. CUDA , und der Benutzer möchte nicht, weiß nicht, wie oder kann dieses defekte Plugin aus Hardwaregründen einfach nicht kompilieren.

Also dann - entweder bleibt der PR ad aeternam in der Schwebe von nie zusammengeführten PRs - oder der Mitwirkende greift nach der umbenannten Variablen in der Quelle des defekten Plugins, ändert den Code, drückt auf seinen Zweig, wartet auf Wenn das CI die Kompilierung abschließt, werden in der Regel mehr Fehler angezeigt, und der Vorgang wird wiederholt, bis CI zufrieden ist.

Keine dieser Optionen ist machbar, aber wir wissen einfach nicht, wie wir es anders machen sollen. Wurden Sie jemals mit einer ähnlichen Situation Ihrer Projekte konfrontiert? Und wenn ja, wie sind Sie mit diesem Problem umgegangen? Gibt es eine Lösung, die ich hier nicht sehe?

Lagarkane
quelle
84
Die oberste Regel für die Bereitstellung einer Plug-in-API für ein System lautet, dass sie stabil oder zumindest abwärtskompatibel bleibt . Änderungen am Core ohne absichtliche Änderungen an der Plugin-API dürfen niemals die Kompilierung von Plugins unterbrechen (es kann vorkommen, dass sie versehentlich die Funktionalität, jedoch nicht die Kompilierung, beeinträchtigen). Wenn eine einfache Änderung eines Variablennamens im Core zu einer fehlerhaften Kompilierung eines Plugins führen kann , scheint die Trennung zwischen Plugins und Core völlig fehlerhaft zu sein.
Doc Brown
Martin Fowlers "Public versus Published Interfaces" könnte eine nützliche Lektüre sein.
Schwern
1
@ KevinKrumwiede: Ich bin mir sicher, dass sie das bereits wissen ;-) Wenn Sie Inkompatibilitäten hatten, bin ich mir ziemlich sicher, dass sie die API absichtlich geändert haben.
Doc Brown
3
Ich würde die Frage umformulieren, da sie wirklich irreführend ist. Etwas wie Wie kann ich PRs verwalten, wenn sie unser aktuelles CI brechen? Erfassen Sie Ihre Situation besser, denke ich.
Bracco23
2
Wie schwierig / komplex ist Ihr Build- / Testprozess? Es sollte nur darum gehen, einen einzelnen Befehl auszuführen oder auf eine einzelne Schaltfläche zu klicken. An diesem Punkt ist es vernünftig zu erwarten, dass Benutzer alle Tests selbst durchführen, bevor sie eine PR einreichen.
Alexander

Antworten:

68

CI-getriebene Entwicklung ist in Ordnung! Das ist viel besser, als keine Tests durchzuführen und kaputten Code einzuschließen! Es gibt jedoch ein paar Dinge, die dies für alle Beteiligten einfacher machen:

  • Festlegen von Erwartungen: Erstellen Sie eine Beitragsdokumentation, aus der hervorgeht, dass CI häufig zusätzliche Probleme findet und diese vor einer Zusammenführung behoben werden müssen. Erklären Sie vielleicht, dass kleinere lokale Änderungen mit größerer Wahrscheinlichkeit gut funktionieren. Daher kann es sinnvoll sein, eine große Änderung in mehrere PRs zu unterteilen.

  • Lokale Tests fördern: Erleichtern Sie das Einrichten einer Testumgebung für Ihr System. Ein Skript, das überprüft, ob alle Abhängigkeiten installiert wurden? Ein Docker-Container, der sofort einsatzbereit ist? Ein Image einer virtuellen Maschine? Verfügt Ihr Testläufer über Mechanismen, mit denen wichtigere Tests priorisiert werden können?

  • Erklären Sie, wie Sie CI für sich nutzen können: Ein Teil der Frustration ist, dass dieses Feedback erst nach dem Einreichen einer PR erfolgt. Wenn die Mitwirkenden CI für ihre eigenen Repositorys einrichten, erhalten sie früheres Feedback - und produzieren weniger CI-Benachrichtigungen für andere Personen.

  • Beheben Sie alle PRs in beiden Fällen: Wenn etwas aufgrund eines Defekts nicht zusammengeführt werden kann und keine Fortschritte bei der Behebung der Probleme erzielt werden, schließen Sie es einfach. Diese aufgegebenen offenen PRs verstopfen einfach alles, und jedes Feedback ist besser, als nur das Problem zu ignorieren. Es ist möglich, dies sehr schön zu formulieren und klar zu machen, dass Sie sich natürlich freuen würden, wenn die Probleme behoben sind. (Siehe auch: Die Kunst des Abschlusses von Jessie Frazelle , Best Practices for Maintainers: Lernen, nein zu sagen )

    Erwägen Sie auch, diese verlassenen PRs auffindbar zu machen, damit andere sie abholen können. Dies kann sogar eine gute Aufgabe für neue Mitarbeiter sein, wenn die verbleibenden Probleme mechanischer Natur sind und keine tiefe Kenntnis des Systems erfordern.

Langfristig gesehen scheinen diese Änderungen die nicht zusammenhängende Funktionalität zu sprengen, was häufig dazu führen kann, dass Ihr aktuelles Design ein wenig problematisch ist. Zum Beispiel, kapseln die Plugin-Interfaces die Interna Ihres Kerns richtig ein? C ++ macht es leicht, versehentlich Implementierungsdetails zu verlieren, ermöglicht aber auch die Erstellung starker Abstraktionen, die nur sehr schwer zu missbrauchen sind. Sie können dies nicht über Nacht ändern, aber Sie können die langfristige Entwicklung der Software hin zu einer weniger fragilen Architektur unterstützen.

amon
quelle
13
"Diese verlassenen offenen PRs machen einfach alles kaputt" Ich wünschte, mehr Betreuer hätten diese Einstellung attitude
GammaGames
34

Um ein nachhaltiges Plugin-Modell zu erstellen, muss Ihr Kern-Framework eine stabile Schnittstelle bereitstellen, auf die sich Plugins verlassen können. Die goldene Regel lautet, dass Sie im Laufe der Zeit neue Schnittstellen einführen können, aber niemals eine bereits veröffentlichte Schnittstelle ändern können. Wenn Sie diese Regel befolgen, können Sie die Implementierung des Kernframeworks beliebig umgestalten, ohne befürchten zu müssen, dass versehentlich Plugins beschädigt werden, unabhängig davon, ob es sich um ein vom Konsortium verwaltetes oder ein externes handelt.

Nach dem, was Sie beschrieben haben, klingt es so, als hätten Sie keine genau definierte Oberfläche, und das macht es schwierig zu sagen, ob eine Änderung die Plugins beschädigen wird. Arbeiten Sie daran, diese Schnittstelle zu definieren und in Ihrer Codebasis explizit anzugeben, damit die Mitwirkenden wissen, was sie nicht ändern sollten.

casablanca
quelle
20
CI sollte automatisierte Tests haben. Wenn Sie sicherstellen möchten, dass Plugins dieselbe Schnittstelle haben, sollte jedes Plugin Tests beisteuern, die die von ihnen benötigte Schnittstelle ausdrücken. Kommen Sie auf diese Weise und wenn sich die Benutzeroberfläche ändert, werden Sie wissen, welche Plugins Sie brechen. Geben Sie mir diese Tests, um sie vor Ort auszuführen, und ich werde wissen, was passiert, bevor ich die PR ausstelle.
candied_orange
1
@lagarkane Klarheit ist eher ein politisches als ein technisches Problem. Es gibt Software, die genau wie Sie das bisherige Verhalten bei einem Upgrade aufgeben. Perl5 ist nicht kompatibel mit Perl6, Python2.7 ist nicht vollständig kompatibel mit Python3.4 usw. Dann gibt es Software, die, was auch immer passiert, noch alten Code unterstützt. Sie können immer noch fast den gesamten Javascript-Code ausführen, der für Netscape Navigator 4 in modernen Browsern geschrieben wurde. Die Tcl-Programmiersprache ist Backwords-kompatibel, also zurück zur Originalversion etc ...
slebetman
3
@lagarkane: Die Bildung des Konsortiums war ein Schritt in die richtige Richtung. Wenn die Kernmitglieder ihre Energie auf das Herausarbeiten dieser Schnittstellen konzentrieren, können Sie die Leistung zukünftiger Doktoranden und Praktikanten nutzen, um Ihr Projekt erfolgreich zu gestalten und gleichzeitig die Anzahl der Unterbrechungen zu minimieren. :)
Casablanca
4
@Fattie: Das funktioniert für Apple, da sie erfolgreiche Produkte für Endverbraucher entwickeln und Entwickler gezwungen sind, mitzuspielen, wenn sie Teil davon sein möchten. Es ist unwahrscheinlich, dass diese Entwickler Änderungen wirklich mögen, und es ist definitiv kein gutes Modell für ein Open-Source-Projekt.
casablanca
1
@casablanca sowohl die MacOS- als auch die WindowsOS-Linie sind äußerst erfolgreich. (Wohl die zwei teuersten Produkte in der menschlichen Existenz.) Über die Jahrzehnte hatten sie absolut gegensätzliche Ansätze. Anscheinend waren beide erfolgreich!
Fattie
8

Um ehrlich zu sein, glaube ich nicht, dass Sie besser damit umgehen können - wenn Änderungen dazu führen, dass verwaltete Teile Ihres Projekts beschädigt werden, sollte das CI fehlschlagen.

Verfügt Ihr Projekt über eine contributing.mdoder eine ähnliche Funktion, die neuen und gelegentlichen Mitwirkenden bei der Vorbereitung ihrer Beiträge hilft? Haben Sie eine klare Liste, welche Plugins zum Core gehören und kompatibel bleiben sollen?

Wenn es aufgrund von Abhängigkeiten usw. schwierig ist, alles auf einem Computer zu erstellen, können Sie einsatzbereite Docker-Images als Build-Umgebungen für Ihre Mitwirkenden erstellen.

mhr
quelle
1
Danke für die Antwort! Ja, wir haben öffentlich zugängliche Richtlinien, aber es werden nicht die von Ihnen vorgeschlagenen Plugins aufgelistet, was bereits eine gute Idee wäre. Das Erstellen von Andockbildern klingt nach einer erheblichen Verbesserung des aktuellen Beitragsprozesses. Vielen Dank für die Eingabe
lagarkane
8

Wenn sie also Änderungen im Core überarbeiten möchten (was heutzutage ziemlich regelmäßig vorkommt), haben sie überprüft, ob der Code auf ihrem Computer kompiliert wurde, bevor sie eine Pull-Anfrage für Github stellten.

Ich denke, hier kann der lockere Stil von Open Source-Projekten ins Wanken geraten. Bei den meisten zentral organisierten Projekten ist das Core Refactoring vorsichtig, insbesondere wenn es eine API-Grenze überschreitet. Wenn sie eine API-Grenze umgestalten, ist dies in der Regel ein "Big Bang", bei dem alle Änderungen gleichzeitig mit einem Inkrement zur Hauptversion der API geplant werden und die alte API beibehalten wird.

Ich würde eine Regel vorschlagen: "Alle API-Änderungen müssen im Voraus geplant werden": Wenn ein PR eingeht, der eine rückwärts inkompatible Änderung an der API vornimmt, muss jemand, der sich nicht mit den Betreuern in Verbindung gesetzt hat, seine Vorgehensweise im Voraus vereinbaren wird einfach geschlossen und der Einsender zeigt auf die Regel.

Sie benötigen auch eine explizite Versionierung der Plugin-API. Auf diese Weise können Sie v2 entwickeln, während alle v1-Plug-ins weiterhin erstellt werden und funktionieren.

Ich würde auch ein bisschen mehr fragen, warum so viele Änderungen am Core-Refactoring und an der API vorgenommen werden. Sind sie wirklich notwendig oder nur Menschen, die dem Projekt ihren persönlichen Geschmack aufzwingen?

pjc50
quelle
2

Klingt so, als müsste der CI-Prozess straffer, umfassender und für die Mitwirkenden sichtbarer sein, bevor sie eine PR erheben. BitBucket verfügt beispielsweise über eine Pipelines-Funktion, die dies ermöglicht. Sie geben ihr eine Datei, die den CI-Erstellungsprozess im Code definiert. Wenn dies fehlschlägt, wird verhindert, dass der Zweig zusammengeführt wird.

Unabhängig von der Technologie können Sie durch das Bereitstellen automatischer Builds, wenn ein Mitwirkender zu einer Zweigstelle wechselt, viel schneller erkennen, worauf Sie beim Vornehmen von Änderungen achten müssen, und dies führt zu PRs, die nachträglich nicht repariert werden müssen.

Designprobleme lassen sich gut beheben, sind jedoch orthogonal zu diesem Problem.

Nathan Adams
quelle
2

Der Code funktioniert, sie sind glücklich, und dann ist das CI fertig und die Probleme beginnen: Die Kompilierung ist in einem vom Konsortium gepflegten Plugin fehlgeschlagen, das der Mitwirkende nicht auf seinem Computer erstellt hat.

Dieses Plugin kann Abhängigkeiten zu Bibliotheken von Drittanbietern aufweisen, z. B. CUDA, und der Benutzer möchte nicht, weiß nicht, wie oder kann dieses defekte Plugin aus Hardwaregründen einfach nicht kompilieren.

Ihre Lösung ist einfach: Senken Sie die Beitragshürde .

Der einfachste Weg, um (1) den Bearbeitungs-Kompilierungs-Testzyklus zu beschleunigen und (2) Umgebungsunterschiede auszugleichen, besteht darin, Build-Server bereitzustellen :

  • Nehmen Sie kräftige Maschinen auf: 24, 48 oder 96 Kerne, 2 GB RAM / Kern, SSD, um die Kompilierung zu beschleunigen.
  • Stellen Sie sicher, dass sie die richtige Hardware haben: FPGA, Grafikkarte, was auch immer benötigt wird.
  • Erstellen Sie ein Docker-Image mit allen vorinstallierten Softwarebibliotheken.

Öffnen Sie dann diese Buildserver für Mitwirkende. Sie sollten in der Lage sein, sich remote in einem neuen Docker-Image anzumelden und den Kompilierungstest auf diesem Computer remote zu bearbeiten.

Dann:

  • Sie haben keine Entschuldigung dafür, die gepflegten Plugins nicht zu erstellen / zu testen: Sie haben alles zur Verfügung.
  • Sie müssen nicht auf langwieriges Feedback mit CI-gesteuerten PRs warten: Sie verfügen über eine inkrementelle Kompilierung und die Fähigkeit zum Debuggen (anstatt zu raten).

Generell können Build-Server von mehreren Mitwirkenden gemeinsam genutzt werden. Wenn jedoch spezielle Hardware-Peripheriegeräte beteiligt sind, kann es erforderlich sein, dass ein Mitwirkender diese Peripheriegeräte selbst verwendet.


Quelle: Bei der Arbeit an Software, die FPGAs verwendet, ist angesichts des Preises der Bestien und der Vielzahl der von uns benötigten Modelle nicht jedes FPGA-Modell auf jedem Entwickler-Computer installiert.

Matthieu M.
quelle
1

Wenn ein Beitrag zum Kern ohne Vertragsänderung abhängige Software zum Erliegen bringen kann, schlägt dies Folgendes vor:

  • Die Verträge Ihrer Schnittstellen sind möglicherweise nicht eindeutig. Möglicherweise hilft das Hinzufügen von Attributen zu Ihren Funktionen und Funktionsparametern dabei, dem Client-Code zusätzliche Einschränkungen aufzuerlegen, um die Verträge klarer zu gestalten. Oder wenn Sie bahnbrechende Änderungen vornehmen, kann die Verwendung der semantischen Versionierung möglicherweise Abhilfe schaffen.
  • Die Unit-Tests decken nicht genügend mögliche Anrufszenarien ab.

Beide Probleme sollten leicht zu lösen sein, Sie erwähnen jedoch, dass das Kernteam möglicherweise nicht über die entsprechenden Kapazitäten verfügt. Eine Möglichkeit wäre, die Community um Hilfe bei der Behebung des Problems zu bitten.

jcayzac
quelle
1

Niemand sonst scheint dies als mögliche Lösung angesprochen zu haben.

  • liste alle Plugins auf, auf die du zugreifen kannst.
  • Führen Sie alle Tests aus, die diese Plugins definieren
  • Zeichnen Sie alle Anfragen / Antworten / Interaktionen zwischen dem Core und allen Plugins auf
  • Speichern Sie diese Aufnahmen, dies sind nun grobe Kompatibilitätstests.

Fordern Sie die Entwickler bei der Entwicklung des Kerns auf, diese Kompatibilitätstests durchzuführen. Wenn sie fehlschlagen, checken Sie nicht ein.

Dadurch wird die Kompatibilität nicht zu 100% sichergestellt, aber es werden viel mehr Probleme und Probleme frühzeitig behoben.

Ein zweiter Vorteil ist, dass diese Aufzeichnungen hervorheben können, welche Schnittstellen aktiv verwendet werden und welche Funktionen aktiv verwendet werden.

Kain0_0
quelle
0

Ich habe Probleme, die Situation so zu verstehen, wie sie zu sein scheint: Das CI baut nur einen Zweig auf?

Gibt es einen Grund, warum Sie mit dem CI nicht mehr als eine Filiale aufbauen können?

Die einfachste Lösung für dieses Problem besteht darin , es jedem Mitwirkenden zu ermöglichen, den CI-Build auf seinem Feature-Zweig auszuführen .

Dann benötigen Sie einfach einen erfolgreichen CI-Build für die Feature-Verzweigung, damit die Pull-Anforderung dieser Verzweigung akzeptiert wird.

Kyralessa
quelle
Dies scheint das Problem zusammenzufassen.
Fattie
1
Die Frage lautet "Oder der Mitwirkende [...] ändert den Code, drückt auf seinen Zweig, wartet, bis das CI fertig kompiliert ist, zeigt in der Regel mehr Fehler an und wiederholt den Vorgang, bis CI zufrieden ist." ist schon der Fall, aber das Problem ist, dass es etwas schmerzhaft ist, sich mit einem so langen Bearbeitungs-Debug-Zyklus zu entwickeln.
npostavs
@npostavs Danke, ich denke, das habe ich beim ersten oder zweiten Mal verpasst, als ich es gelesen habe. Trotzdem ... ich glaube, ich scheine nicht das Problem zu sein. Es gibt viele Abhängigkeiten, sie können nicht gebrochen werden, daher muss ein Mitwirkender mit allen kompatibel bleiben. Das ist die Natur großer Software. Sicherlich könnte man daran arbeiten, den Build zu beschleunigen, aber ansonsten, welche Verknüpfung könnte es geben?
Kyralessa