Wie viele Entwickler werden vor der kontinuierlichen Integration für uns wirksam?


34

Die kontinuierliche Integration ist mit einem Mehraufwand verbunden, z. B. Einrichtung, Umschulung, Sensibilisierungsmaßnahmen, Unterbrechung der Behebung von "Fehlern", die sich als Datenprobleme herausstellen, erzwungene Trennung der Programmierstile von Bedenken usw.

Ab wann rechnet sich die kontinuierliche Integration?

EDIT: Das waren meine Erkenntnisse

Das Setup war CruiseControl.Net mit Nant, das von VSS oder TFS liest.

Hier einige Gründe für einen Fehler, die nichts mit dem Setup zu tun haben:

Untersuchungskosten : Die Zeit, die aufgewendet wird, um zu untersuchen, ob ein rotes Licht auf eine echte logische Inkonsistenz im Code, in der Datenqualität oder auf eine andere Ursache zurückzuführen ist, z ist ausgefallen usw. usw.)

Politische Kosten über der Infrastruktur : Ich habe überlegt, für jede Methode im Testlauf eine "Infrastruktur" -Prüfung durchzuführen. Ich hatte keine Lösung für das Timeout, außer den Build-Server zu ersetzen. Bürokratie störte und es gab keinen Serverersatz.

Kosten für die Korrektur von Komponententests : Ein rotes Licht aufgrund eines Datenqualitätsproblems kann ein Hinweis auf einen schlecht geschriebenen Komponententest sein. Daher wurden datenabhängige Komponententests neu geschrieben, um die Wahrscheinlichkeit eines roten Lichts aufgrund schlechter Daten zu verringern. In vielen Fällen wurden die erforderlichen Daten in die Testumgebung eingefügt, um die Komponententests genau ausführen zu können. Es ist sinnvoll zu sagen, dass der Test robuster wird, wenn er die Daten robuster macht, wenn er von diesen Daten abhängig ist. Das hat natürlich gut funktioniert!

Deckungskosten, dh Schreiben von Komponententests für bereits vorhandenen Code : Es bestand das Problem der Abdeckung durch Komponententests. Es gab Tausende von Methoden, die keine Unit-Tests hatten. Daher würde eine beträchtliche Anzahl von Manntagen benötigt, um diese zu erstellen. Da dies zu schwierig wäre, um ein Geschäftsmodell zu erstellen, wurde beschlossen, Unit-Tests für künftige neue öffentliche Methoden zu verwenden. Diejenigen, die keinen Komponententest hatten, wurden als "potentiell infrarot" bezeichnet. Ein interessanter Punkt hierbei ist, dass statische Methoden ein Diskussionspunkt dafür waren, wie es möglich wäre, eindeutig zu bestimmen, wie eine bestimmte statische Methode fehlgeschlagen ist.

Kosten für maßgeschneiderte Releases : Nant-Skripte gehen nur so weit. Sie eignen sich beispielsweise nicht für CMS-abhängige Builds für EPiServer, CMS oder eine UI-orientierte Datenbankbereitstellung.

Dies sind die Arten von Problemen, die bei stündlichen Testläufen und QS-Builds über Nacht auf dem Buildserver aufgetreten sind. Ich gehe davon aus, dass diese als Build-Master unnötig sind und diese Aufgaben zum Zeitpunkt der Veröffentlichung manuell ausführen können, insbesondere mit einer Ein-Mann-Band und einem kleinen Build. Einzelschritt-Builds haben meiner Erfahrung nach die Verwendung von CI nicht gerechtfertigt. Was ist mit den komplexeren, mehrstufigen Builds? Dies kann eine Herausforderung sein, insbesondere ohne ein Nant-Skript. Also waren diese nicht mehr erfolgreich, obwohl sie eines geschaffen hatten. Die Kosten für die Behebung der Rotlichtprobleme überwogen die Vorteile. Schließlich verloren die Entwickler das Interesse und stellten die Gültigkeit des roten Lichts in Frage.

Nach einem fairen Versuch bin ich der Meinung, dass CI teuer ist und viel an den Rändern gearbeitet wird, anstatt nur die Arbeit zu erledigen. Es ist kostengünstiger, erfahrene Entwickler zu beschäftigen, die keine großen Projekte durcheinander bringen, als ein Alarmsystem einzuführen und zu warten.

Dies ist auch dann der Fall, wenn diese Entwickler das Unternehmen verlassen. Es spielt keine Rolle, ob ein guter Entwickler das Unternehmen verlässt, da durch die von ihm befolgten Prozesse sichergestellt wird, dass er Anforderungsspezifikationen und Entwurfsspezifikationen schreibt, die Codierungsrichtlinien einhält und seinen Code so kommentiert, dass er lesbar ist. All dies wird überprüft. Wenn dies nicht geschieht, erledigt sein Teamleiter seine Arbeit nicht. Dies sollte von seinem Vorgesetzten usw. übernommen werden.

Damit CI funktioniert, reicht es nicht aus, nur Komponententests zu schreiben, die vollständige Abdeckung beizubehalten und eine funktionierende Infrastruktur für große Systeme sicherzustellen.

Fazit: Man könnte sich fragen, ob die Behebung möglichst vieler Fehler vor der Veröffentlichung aus betriebswirtschaftlicher Sicht überhaupt wünschenswert ist. CI erfordert viel Arbeit, um eine Handvoll von Fehlern zu erfassen, die der Kunde in UAT identifizieren oder das Unternehmen für die Behebung im Rahmen eines Kundendienstvertrages bezahlen kann, wenn die Garantiezeit ohnehin abläuft.


13
Es kann sogar für ein Team von einem Vorteil sein. Insbesondere wenn Sie eine Test-Suite mit langer Laufzeit haben, ist es viel besser, die Ergebnisse eines Builds und Testlaufs über Nacht automatisch zu erhalten, als dies die ganze Zeit manuell zu tun.
SK-logic

3
@Carnotaurus, lokaler Klon des entfernten Git-Repositorys, beseitigt das Zeitlimit der Quellcodeverwaltung. Netzwerkprobleme - für den Aufbau des Produkts? Nun wirklich ...

3
@Carnotaurus rotes Licht aufgrund von Datenqualität oder Infrastruktur ist ein Code-Modell von Fragile Tests . Siehe auch so: Verwalten der Wartungslast von
Komponententests

1
Je mehr ein Test von äußeren Aspekten abhängt, desto zerbrechlicher ist er. Beispiel: Wenn ein Test nur erfolgreich ist, wenn sich Kunde XY in der Datenbank befindet, ist der Test fragil, es sei denn, der Testaufbau stellt sicher, dass diese Voraussetzung besteht, indem er die Daten selbst einfügt, falls erforderlich.
k3b

6
"Fazit: Warum funktionsfähige Produkte herstellen, wenn wir jemanden veranlassen können, uns für die Reparatur zu bezahlen, weil er nicht erwartet hat, dass die Software überhaupt funktioniert"? Es entspricht möglicherweise nicht der gesetzlichen Definition, aber das klingt für mich nach Betrug.
Chris Pitman

Antworten:


43

Das Einrichten eines CI-Motors entspricht dem Einrichten eines Feuermelders in einem Haus.

Meiner Meinung nach korrelieren die Vorteile nicht mit vielen Entwicklern, sondern mit einer großen Codebasis. Die CI-Engine erledigt aktiv alle langweiligen Arbeiten, die Sie nicht selbst ausführen möchten, und dies jedes Mal.

Wenn Sie ein Remote-Modul beschädigen, das Sie lange nicht mehr berührt haben, werden Sie sofort darüber informiert. Nicht nur in Bezug auf die Kompilierung, sondern auch in Bezug auf die Funktion, wenn Sie Unit-Tests eingerichtet haben.

Beachten Sie auch , dass , wenn Sie lassen Sie CI-Motor tun alle die langweilige Arbeit, einschließlich Installateure usw. einrichten, Sie haben es nicht manuell zu tun. Sie können einfach Ihre Quelle einchecken und darauf warten, dass das fertige Produkt am Standardstandort hergestellt wird. (BEARBEITEN: Die CI-Engine arbeitet auch in einer genau definierten Umgebung, wobei keine Entwicklereinstellungen erforderlich sind, um die Reproduzierbarkeit zu gewährleisten.)

Dies ist auch Teil der Qualitätssicherung.


BEARBEITEN: Nachdem ich das oben Genannte geschrieben habe, habe ich Erfahrung mit dem Maven Build Tool für Java. Dies ermöglicht es uns im Wesentlichen, die gesamte CI-Konfiguration innerhalb des Projekts (mit pom.xml) beizubehalten, was die Wartung der CI-Installation und / oder die Migration auf eine andere CI-Engine erheblich vereinfacht.


1
@Carnotaurus, in diesem Fall wird das rote Licht auch für vorübergehende Fehler verwendet. Klingt nach einer Einschränkung der von Ihnen verwendeten CI-Engine.

2
@Carnotaurus Nach meiner Erfahrung ist die Arbeit, alle Aspekte von allem, was z. B. das Ausführen eines Releases und das wiederholte Ausführen eines Releases betrifft, gründlich zu dokumentieren, größer als die Erfassung des Workflows in einem Ameisen- oder Maven-Skript, das dann von einem Roboter ausgeführt werden kann beliebig oft. Der Roboter arbeitet auch in einer Reinraumumgebung und stellt sicher, dass die Builds reproduzierbar sind.

1
Beachten Sie, dass die von mir gesuchte Unterbrechung keine fehlgeschlagenen Komponententests sind, sondern dass die von uns gelieferten Programme weiterhin erstellt werden können. Es ist nicht mehr so ​​wichtig, dass wir auf Maven-Artefakte migriert sind, anstatt alles für jedes Release zu kompilieren, aber es ist immer noch wichtig zu wissen, dass der Build voll funktionsfähig ist. Wir brauchen nur den Continuous Deployment-Teil.

2
@Carnotaurus: Ich spreche nicht von einem Alarmsystem. Wenn die Tests fehlschlagen, wird der Patch (überhaupt) nicht in das Haupt-Repository integriert. So wird sichergestellt, dass Sie beim Auschecken / Klonen eine voll funktionsfähige Kopie erhalten und sofort mit der Arbeit beginnen können. Nun, ich stimme Ihnen zu, dass Tests schwierig zu schreiben und zu warten sein können ... aber ich habe mit und ohne Test gearbeitet und sehe eine deutliche Qualitätsverbesserung bei ihnen. Die Frage ist also: automatisiert oder nicht? Nach meiner Erfahrung dauert das manuelle Testen ungefähr so ​​lange wie das Schreiben des Automatisierungsskripts (aber ich arbeite in einem großen Unternehmen mit entsprechenden Tools).
Matthieu M.

5
" Es ist eine Menge Arbeit, eine Handvoll Fehler zu erfassen, für deren Behebung das Unternehmen ohnehin im Rahmen eines Kundenservicevertrags bezahlt wird. " In diesem Fall besteht ein wirtschaftlicher Anreiz, KEINE Software mit möglichst wenigen Fehlern zu erstellen in diesem Fall trifft dies alles nicht auf Sie zu.

33

Es ist nicht die Anzahl der Entwickler, sondern die Anzahl der Schritte, die erforderlich sind, um von 1 zu n (einschließlich) zu gelangen, wobei 1 & n ...

1: Code auschecken
und
n: installierbare \ implementierbare Pakete haben

Wenn n <2, benötigen Sie möglicherweise kein CI.
Andernfalls benötigen Sie ein CI

Update Nach dem
Lesen Ihrer Ergebnisse kann ich nur den Schluss ziehen, dass Sie sich aus den falschen Gründen an CI gewandt haben.


1
+1 - Ich könnte noch einiges hinzufügen - aber das ist sehr nah am Kern, warum ich CI mag ... nicht nur für das, was es tut, sondern auch für das, was Sie tun müssen, um es zu machen Arbeit (diese Anforderungen sind Dinge, die Sie sowieso wirklich tun sollten).
Murph

2
@murph: Yup, und es bringt kleine Vorteile wie 1) zu wissen, was in Ihrem Repository enthalten ist, 2) mit einem Klick zu erstellen, 3) in der Lage zu sein, eine Version in
Kürze

Man kann also sagen, dass dies von der Komplexität der Veröffentlichung abhängt, gemessen an der Anzahl der Schritte bis zur Bereitstellung, die es ermöglichen, separate Ebenen zu "testen"?
CarneyCode

1
@ Karnotaurus: Entschuldigung Kumpel, aber ich bin nicht sicher, ob ich weiß, was Sie sagen wollen. CI hat nichts mit Testen zu tun. Ja, Sie können und sollten Komponententests als Teil des Builds ausführen, sie sollten jedoch nicht von etwas abhängig sein, das nicht als Teil des Tests eingerichtet wurde. CI hilft jedoch beim Testen. Einer der vielen Vorteile von CI ist, dass ein QA-Team sofort und ohne großen Aufwand neue Codeänderungen erfassen kann. CI = Die Fähigkeit zur sofortigen Veröffentlichung
Binary Worrier

1
@ BinaryWorrier - Ich denke, Schritt 1 ist das Einchecken des Codes;)

10

Auch für ein Team von einem kann sich die Mühe lohnen. Dies gilt insbesondere dann, wenn Sie plattformübergreifenden Code entwickeln und sicherstellen müssen, dass Ihre Änderungen auf beiden Plattformen funktionieren. Zum Beispiel ist der C ++ - Compiler von Microsoft akzeptabler als GCC. Wenn Sie also unter Windows entwickeln, aber auch Linux unterstützen müssen, ist es eine große Hilfe, wenn Sie von einem CI-System erfahren, wann Ihr Build unter Linux abbricht.

Einige CI-Systeme sind recht einfach einzurichten, sodass der Overhead nicht wirklich so groß ist. Probieren Sie zum Beispiel Jenkins oder Hudson.


Ich hatte kein plattformübergreifendes Szenario in Betracht gezogen. Wenn verschiedene Compiler benötigt werden, um verschiedene Builds zu erstellen, gibt es ein starkes Argument für CI - nehmen Sie eine positive Stimme.
CarneyCode

4

Wie Sie sagen, entstehen zusätzliche Kosten für die Einrichtung und den Betrieb.

Die Frage, wo die Gewinnschwelle liegt, hängt jedoch nicht davon ab, wie viele Personen in Ihrem Team beschäftigt sind, sondern von der Länge Ihres Projekts.

Allerdings gibt es einen Teil der Einrichtungskosten, die Sie in all Ihren zukünftigen Projekten verwenden können, sodass die Gemeinkosten langfristig gegen Null gehen können.


So ist Länge des Projekts (Größe des Projekts) für Sie CI wichtig? Ich habe festgestellt, dass die Fehlalarme sehr kostspielig sind.
CarneyCode

Ja, Sie müssen die Zeit haben, um die Kosten für Einrichtung und Lernkurve zurückzuzahlen. Theoretisch sollten Sie im Laufe der Zeit lernen, wie Sie die Fehlalarme beseitigen.
Stephen Bailey

Ja, das ist Theorie
CarneyCode

Nun, nein, es ist Praxis. Mit der Zeit notieren Sie, welche Tests fragil sind und welche nicht, und Sie machen sich keine allzu großen Sorgen, wenn Ihre fragilen Tests brechen, es sei denn, sie brechen mehrmals hintereinander. Sie schreiben mehr isolierte, robuste Tests, während Sie lernen, wie es geht, und lassen die bisherige Abdeckung im Laufe der Zeit aufbauen, anstatt alles auf einmal zu tun. In der Praxis ist CI kein Wundermittel, sondern eine Prozessänderung, die einige Zeit in Anspruch nimmt und letztendlich zu weniger fehlerhafter Software führt.
Philosodad

3

Ich habe Jenkins diese Woche eingerichtet, um ein kleines .NET-Projekt zu erstellen, an dem ich arbeite. Ich habe es in meine Git-Quellcodeverwaltung integriert, sodass bei jedem Commit ein Build ausgelöst wurde. Ich habe die Unit-Tests in den Build integriert. Ich habe die statische Analyse in Form von FxCop- und StyleCop-Verstößen integriert.

Jedes Mal, wenn ich einchecke, checkt es meinen gesamten Code aus, erstellt ihn, erhöht die Versionsnummer für alle Assemblys, testet ihn, analysiert ihn auf FxCop- und StyleCop-Verstöße, archiviert den Build und zeichnet die Ergebnisse in einer Reihe von Diagrammen auf Ich habe im Laufe der Zeit Einblick in Testergebnisse und Verstöße.

Dies von Grund auf zu tun, dauert ungefähr eine Stunde (vielleicht einen Tag mit Google, wenn Sie es noch nicht getan haben). Es kostet nichts, da alle Tools kostenlos zur Verfügung stehen.

Wenn, wie und wann das Projekt erstellt wird, habe ich eine qualitativ hochwertige Infrastruktur, die kostenlos mitwächst. Wenn oder wenn neue Entwickler dem Projekt beitreten, kann ich kostenlos einen vollständigen Überblick über ihre Arbeit und ihre Auswirkungen auf das Projekt erhalten.

Das einzige Szenario, in dem ich sehe, dass sich CI nicht lohnt, ist entweder ein Projekt, das einen Tag in Anspruch nimmt und dann nie wieder aufgegriffen wird, oder eines in einer Sprache, in der keine vorhandenen / kostenlosen Tools verfügbar sind und deren Anschaffungskosten ist unverhältnismäßig zur Arbeit.


Wie ich bereits sagte, sind es die Komponententests, mit denen der Legacy-Code abgedeckt wird, die die Hauptkosten verursachen. Auch hier geht es nicht um ein Ein-Mann-Team ... Ich schätze die Köpfe auf Jenkins.
CarneyCode

1
Für das, was es wert ist, habe ich einen beträchtlichen Vorteil daraus gezogen, dass ich einen CI-Server ohne Tests ausgeführt habe - allein aufgrund des Vertrauens in saubere Builds und der Verfügbarkeit von Bereitstellungspaketen.
Murph

1

Wenn Sie nach jeder Änderung alle Aspekte Ihres Projekts überprüfen können, benötigen Sie kein CI.

In allen anderen Fällen ist es ein Nettogewinn.


Ich denke, hier komme ich auch her. Es ist auch viel billiger.
CarneyCode

Was meinst du mit Verifikation in diesem Fall? Wenn es automatisiert ist, so oft wie möglich vorkommt und dabei hilft, geistige Ausrutscher und Versehen zu identifizieren, dann bin ich alles dafür. :-)
William Payne

1

Der Aufwand ist minimal. Ich würde sagen, für Ein-Mann-Projekte wäre es nützlich. Sobald Sie zwei erreichen, ist es von unschätzbarem Wert.

Ich bin damit einverstanden, dass Sie für Ein-Mann-Projekte, wenn Sie einen "One-Step-Build / Verify" -Projekt haben, mit der kontinuierlichen Integration einverstanden sind es in einem formalen System (CC, Hudson, TeamCity, etc.).


Du meinst ohne CI?
CarneyCode

1

Die Frage, wann sich CI amortisiert, ist bei einem neuen Projekt nur schwer zu beantworten.

In einem vorhandenen Projekt ist es viel einfacher zu sehen. Wenn Sie einen kritischen Fehler in der Entwicklung der Lieferkette finden, wissen Sie, dass das Problem zum frühestmöglichen Zeitpunkt vorliegt. Die Kosten für das Reparieren sind jetzt die niedrigsten. Wenn dieses Problem in einer Umgebung auftritt, die über der Entwicklung liegt, sind Ihre Kosten höher.

Nun, das Management könnte beschließen, mit einem Fehler zu veröffentlichen, aber das ist ein Geschäftsrisiko. Zumindest kann dies jetzt durch diese Risikobewertung gemildert werden, anstatt durch spätabendliche Telefonate / Paniken, die, wenn sie zu Stundensätzen abgerechnet werden, sehr teuer werden.

Eine andere Sache, um in Bezug auf die Kosten zu berücksichtigen. Wie ist Ihre QS-Abteilung? Handelt es sich um manuelle Tests? Wenn Sie sich für CI entscheiden, können Sie möglicherweise die QA-Gesamtkosten senken, indem Sie diese Skripts für Ihre Entwicklungsbox automatisieren. Möglicherweise können Sie die Anzahl der QS-Mitarbeiter reduzieren, die Ihr Projekt unterstützen, indem Sie sie in die CI-Phase einbeziehen.


1
Das Ersetzen eines manuellen Regressionstests durch einige automatisierte Tests der Benutzeroberfläche erfordert viel Zeit und Können. In einer Firma schrieb ein Kerl ungefähr 40% der Tests und verließ die Firma. Einige Jahre später wurden die Tests noch nicht geschrieben. Ich wette, das ist typisch.
CarneyCode

Nein, das ist nicht typisch.
quant_dev

Ich habe nicht viele Gegenbeweise gesehen
CarneyCode

Wenn Sie Integrations- / Akzeptanztests in natürlichem DSL wie Gherkin schreiben, können Sie den automatisierten Test einfach in manuell übersetzen. Ich sehe das nicht als Problem.
Mike Cornell

@ Karnotaurus - Ich denke, es ist typisch. Ich habe es auch zweimal gesehen. Automatische Tests der Benutzeroberfläche sind schwierig.
Rocklan

1

CI lohnt sich immer immer: Durch das Sicherheitsgefühl, das es Ihnen gibt, können Sie schneller arbeiten, als es sonst möglich wäre. Das Problem, das Sie haben, scheint sich um Unit-Tests zu drehen. Ich stimme zu, dass Unit-Tests sehr teuer sind, denke aber auch, dass sie (für viele Dinge) die am wenigsten schlimmste Option sind, die wir haben. Persönlich und für die Arten von Systemen, an denen ich tendenziell arbeite, schwöre ich auf Tests auf Systemebene, bei denen eine Kombination aus realen und (möglicherweise synthetischen) pathologischen Fällen angewendet wird. Es ist billiger als Unit-Tests und gelangt in die schwer zugänglichen Ecken Ihres konzeptionellen Universums: Donald Rumsfelds "Unknown Unknowns".


Ich mag das bisschen über UI-Tests auf Systemebene. Ein Großteil der Tests geht in einem Nebel von Unit-Tests mit fragwürdiger Qualität und Abdeckung verloren.
CarneyCode

Berichterstattung ist auch eine Frage der menschlichen Psychologie; Wir haben die angeborene Tendenz, Tests für die Fälle zu schreiben, über die wir bereits nachgedacht haben. Aus diesem Grund müssen Unit-Tests von einem anderen Team als dem, das das zu testende System entwickelt hat, geschrieben werden, um auf einem hohen SIL-Niveau zertifiziert zu werden. Selbst dann gibt es viele Fälle und Situationen, die nur für jedermann offensichtlich sind im Rückblick. Eine strenge Überprüfung der Codeabdeckung ist erforderlich. aber außerordentlich schwierig und teuer.
William Payne

... Daher der Vorteil, den Sie erhalten, wenn Sie den scheußlichsten Müll, den Sie finden können, auf der obersten Ebene in das System werfen und sehen, was passiert ... :-)
William Payne

1
+1 - Stimme voll und ganz zu. Einige Teile eines Systems sind einfach nicht Unit-testbar (dh die Kanten wie in der Datenbank). Sicher können Sie die Datenbank verspotten, aber das lässt den Code, der mit der Datenbank spricht, ungetestet. Irgendwann müssen Sie einige reale Tests schreiben, die Ihre Systeme integrieren und mit anderen Systemen kommunizieren. Wenn Sie das tun, finden Sie immer Fehler, die Sie in der gemütlichen, übersichtlichen Welt des Komponententests und des spöttischen Frameworks übersehen haben (LINQ to SQL fällt Ihnen ein).

Tatsächlich habe ich kürzlich eine interessante Einstellung
William Payne

0

Verwenden Sie es immer, unabhängig von der Größe des Teams. Wenn es zum Beispiel nur Sie sind, wer weiß, könnten Sie bei Starbucks von Ihrem Laptop aus codieren und dann zu Hause von Ihrem stärkeren System aus fortfahren?

Der Aufwand ist wirklich nicht so schlimm.


0

Eins. Ja, man reicht aus, um mit der kontinuierlichen Integration zu beginnen.


0

Es geht nicht darum, wie viele Entwickler, sondern um

ein. Wie viel Zeit sparen Sie durch Automatisierung und wie oft.

  • Zeitersparnis beim Erstellen nach Integrationen, Ausführen automatisierter Tests und Erstellen von Setups * Häufigkeit des Eincheckens = Zeitersparnis in Prozent.

b. Wie viele Versionen / Branchen / Produkte haben Sie?

  • Wenn Sie einen Entwickler haben, der an zwei verschiedenen Zweigen arbeitet, verdoppelt sich die Zeitersparnis, da jeder Zweig gebaut, getestet und verpackt werden muss.
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.