- Manche Softwareprojekte sind warmblütig und müssen durch kontinuierliche Entwicklungsaktivität am Leben gehalten werden; wenn die Arbeit zwar unterbrochen wird, später aber wieder aufgenommen werden kann, sind sie eher kaltblütig
- Kaltblütige Projekte wählen langweilige Technik, damit Build und Tests auch nach langer Pause nicht kaputtgehen, und vermeiden Abhängigkeiten von externen Diensten, die sich ändern oder verschwinden können
- Die Übernahme oder Schließung abhängiger Dienste, Compiler-Upgrades und eingestellter Paket-Support schlagen beim Wiederanlaufen wenig aktiver Projekte als Wartungskosten zu Buche
- Code, den man wie bei persönlichen Projekten 1, 2 oder 3 Jahre lang nicht anfasst, kann nur schwer kontinuierliche Wärme erzeugen; deshalb sollte man ihn von Anfang an unter der Annahme einer niedrigen Änderungsrate entwerfen
- Der statische Site-Generator für diesen Blog läuft seit dem ersten Commit im Jahr 2012 mit Python 2, vier im Repository enthaltenen Drittanbieter-Modulen, lokaler Ausführung und Deployment per
rsyncoversshbis heute fast ohne Änderungen weiter
Projektpflege im Bild kaltblütiger Tiere
- In einer naturkundlichen Vorlesung im Jahr 2004 legte der Professor ein aus dem Gefrierfach geholtes painted turtle Jungtier unter die Kamera und hielt damit die Vorlesung
- Das Jungtier der painted turtle war eine von nur wenigen Arten, die selbst im gefrorenen Zustand überleben konnten
- Innerhalb einer Stunde begann die Schildkröte mit kaum sichtbaren Bewegungen und hatte sich am Ende etwa über die Hälfte des Bildschirms bewegt
- Warmblütige Tiere müssen ihre Körpertemperatur in einem engen Bereich halten; beim Menschen wird es problematisch, wenn man sich von etwa 37°C entfernt
- Kaltblütige Tiere regulieren ihren Stoffwechsel passend zur Umgebungstemperatur, sind bei Wärme aktiv und bewegen sich immer langsamer, je kälter Körper und Umwelt werden
- Softwareprojekte lassen sich ähnlich einteilen
- Warmblütige Software funktioniert gut, wenn im Projekt kontinuierlich Bewegung und Wärme vorhanden sind
- Lässt man sie 6 Monate ruhen, kann sie beim erneuten Hervorholen wie ein totes Projekt wirken
Bedingungen und Beispiele für kaltblütige Software
- Dass warmblütige Projekte sich nur schwer wieder starten lassen, liegt daran, dass sich externe Veränderungen ansammeln
- Dienste, von denen die CI abhängt, können übernommen worden sein oder aus Geldmangel nicht mehr funktionieren
- Beim Hinzufügen neuer Abhängigkeiten kann ein Compiler-Upgrade nötig werden
- Andere Pakete werden womöglich nicht mehr gepflegt und funktionieren mit dem neuesten Compiler nicht mehr
- Projekte, an denen man allein arbeitet, nur bei Inspiration Änderungen vornimmt und dann länger als ein Jahr nicht mehr anfasst, lassen sich schwer als warmblütige Projekte betreiben
- Ein kaltblütiges Projekt sollte wie das gefrorene Jungtier der painted turtle auch nach einem Jahr noch genau an der Stelle wieder starten können, an der es angehalten wurde
- Dafür verwendet man boring technology und vermeidet Abhängigkeiten von externen Diensten, bei denen Build- und Testskripte sich ändern, kaputtgehen oder ganz verschwinden können
- Abhängigkeiten werden wie bei vendored dependencies direkt im Projekt-Repository mitgeführt
- Die Software, die diesen Blog betreibt, ist ein Beispiel für ein kaltblütiges Projekt
- Der erste Commit stammt vom 8. Januar 2012; es war ein kleiner statischer Site-Generator, der eine alte Wordpress-Installation ersetzen sollte
- Er wurde in Python 2 geschrieben, hängt von vier Drittanbieter-Modulen ab, die alle ins Projekt-Repository eingecheckt sind
- Alle Schritte laufen lokal, und die Ergebnisse werden per
rsyncoversshdeployt - Abgesehen von einigen kleinen Verbesserungen läuft er seitdem ohne Änderungen weiter, und es wird erwartet, dass er auch in 12 Jahren noch funktioniert
1 Kommentare
Hacker-News-Meinungen
Das Web-Framework Express aus dem Node- und JavaScript-Ökosystem wird aktuell in der Hauptversion 4.x.x seit über 10 Jahren gepflegt https://www.npmjs.com/package/express?activeTab=versions
Trotzdem wird es mit über 17 Millionen Downloads pro Woche sehr häufig genutzt https://www.npmjs.com/package/express, und auch wenn ihm Funktionen fehlen oder die Performance nicht die beste ist https://fastify.dev/benchmarks/, ist es gut, weil es schnelle und stabile Entwicklung sowie langfristige Planung ermöglicht
Man muss sich keine Sorgen machen, dass Sicherheits-Patches für ältere Versionen eingestellt werden oder sich APIs drastisch ändern; Go ist dank seiner umfangreichen Standardbibliothek und des Kompatibilitätsversprechens noch stabiler, sodass auch über 10 Jahre alte Programme noch laufen können https://go.dev/doc/go1compat
Als es erstmals erschien, wurde es oft allein deshalb, weil es in JavaScript geschrieben war, als Schrott für minderwertige Pseudo-Programmierer abgetan; seitdem hat es aber in mehreren Unternehmen geholfen, großartige Services zu bauen, die mit Express echtes Geld verdienen, und heute verarbeitet es vermutlich enorme Mengen an Requests
Heutzutage schreibe ich auch viel in Go, bin aber weiterhin vollkommen zufrieden damit, Services mit Express zu bauen, und halte es insgesamt für gute Software
Nicht, dass ich es wirklich ablehne; ich würde es wohl eher wegen des endlosen Hamsterrads ständiger Versions-Upgrades nicht wählen
Python ist ein sehr schlechtes Beispiel für kaltblütige Software
Sowohl bei der Runtime als auch bei den Tools gibt es ständig Breaking Changes, und der Autor ist offenbar in einer Situation, in der er weiterhin Python 2 verwenden muss, dessen Support schon vor langer Zeit eingestellt wurde
Bessere Beispiele wären Sprachen wie Go oder Java, bei denen 10 Jahre alter Code auch mit modernen Tools gut läuft; noch extremer wäre Perl, wo sogar 30 Jahre alter Code noch gut funktioniert
Beim Entwickeln von Software macht man Fehler, durch die Nutzer Dinge auf eine nicht beabsichtigte Weise tun können; in der Java-Welt löst man das, indem man neuere, sicherere und klarer intendierte Funktionen hinzufügt und Nutzer zur Migration ermutigt
Python ist ähnlich, nur kommt dort noch ein „und bald schalten wir die alte Funktion ab“ dazu, während Java das nicht tut
Zum Beispiel gilt die
equals-Methode vonjava.net.URLals kaputtes Design und wird stark abgeraten, wird aber seit über 20 Jahren weiter unterstütztDer leere Operator in Python Airflow unterstützte eine Zeit lang den Namen
DummyOperator, aber weil „dummy“ historisch und kulturell auch als abwertender Begriff verwendet wurde, änderten die Maintainer ihn so, dassEmptyOperatorgenutzt werden sollte, und brachen den alten NamenNach einem Upgrade gab es beim Laden des Codes Fehler, bis man die Referenznamen änderte; persönlich würde ich Nutzer nicht auf diese Weise brechen
In der Java-Welt hätte man eine bloße Namensänderung mit einer Textersetzung ausreichend bewältigen können, also hätte man sie wohl weiter unterstützt, bis es einen wirklich zwingenden Grund gegeben hätte, das nicht mehr zu tun
Deshalb denke ich insgesamt, dass sich Java und Java-Library-Abhängigkeiten deutlich freier upgraden lassen als Python
Wenn man eine Java-LTS-Version nutzt und gute Abhängigkeiten auswählt, kann man es jederzeit wieder lauffähig machen
In Python gab es in einem Machine-Learning-Kurs einmal den Fall, dass eine Abhängigkeit über Nacht eine API-Änderung einführte, die alles kaputt machte; der Dozent nutzte noch die neueste Version von vor einigen Wochen, als er mit der Vorbereitung des Kurses begonnen hatte, und hatte es deshalb nicht bemerkt
Der Wechsel von Python 2 zu 3 war ein Breaking Change, aber das war eine einmalige Änderung und keine „fortlaufenden Breaking Changes“
Wenn man bei derselben Hauptversion bleibt, wird alter Code durch neue Minor-Versionen nicht kaputt gemacht; alter 2.x-Code läuft zum Beispiel gut auf 2.7, und alter 3.x-Code läuft auch auf 3.12 gut
Minor-Versionsänderungen können neue Funktionen hinzufügen, aber alter 3.x-Code geht nicht kaputt, nur weil er das Schlüsselwort
asyncoder Type Hints nicht verwendetDas ist einer der Gründe, warum ich Python nach Möglichkeit meide
Ich habe das Gefühl, dass Python-Code, den ich heute schreibe, in ein paar Jahren mit eher geringer Wahrscheinlichkeit noch funktioniert, und halte das für ein ziemlich großes Problem
Selbst wenn ich versucht habe, 3 Jahre alten Java-Code mit einem neuen SDK auszuführen, war immer irgendwo etwas kaputt
Ich arbeite auf IBM-Mainframes (z/OS), und bei der Wahrung der Abwärtskompatibilität habe ich kaum jemanden gesehen, der IBM nahekommt
Microsoft Windows sehe ich auf Platz 2, das Linux-Kernel-ABI vielleicht auf Platz 3, aber im gesamten Linux-Ökosystem ist das nur ein kleiner Teil
Das meiste andere ist eher Churn, und in Open Source scheint es nur wenige Leute zu geben, die ihre Freizeit in Abwärtskompatibilität stecken wollen
Ökonomisch wirkt das wie ein Gefangenendilemma: Alle schieben die Kosten für Kompatibilität auf andere ab, und am Ende erzeugen sie für alle mehr nutzlose Arbeit
Schaut man sich zum Beispiel die Retro-Computing-Community an, ist es gar nicht so selten, dass Treiber geschrieben werden, damit neue Hardware auf alten Betriebssystemen läuft
Wenn es keine Vergütung gibt, hängt es letztlich davon ab, wie sehr man die Plattform liebt, die man baut; ich habe mich wegen des bewährten Commitments zur ABI-Stabilität entschieden, den Linux-Kernel direkt über Systemaufrufe anzusprechen
Bei meiner eigenen Programmiersprache hingegen möchte ich sie so „perfekt“ wie möglich machen und habe ständig das Bedürfnis, sie zu ändern
Für den Fall, dass jemand auf die wahnsinnige Idee kommt, sie ernsthaft zu nutzen, habe ich in der README einen Hinweis platziert, dass sie noch in einer frühen Entwicklungsphase und instabil ist
Ich vermute, die Leute, die Ruby oder Python entwickeln, empfinden ähnlich: Eine Sprache fühlt sich wie ein eigenes Kind an, man will, dass sie Erfolg hat, und hält es daher für nötig, Fehler wie damals, als
printein Keyword war, zu korrigierenOft gehen sogar genau die Teile kaputt, die für Abwärtskompatibilität gedacht sind
Bei einem früheren Arbeitgeber bauten wir containerisierte Node-Apps, und CI erstellte Images aus dem Node-Quellcode; nach einer Weile begannen Deployments von Services, die niemand angerührt hatte, plötzlich zu scheitern
Es stellte sich heraus, dass das Dockerfile auf einem Ubuntu-Image basierte, dessen Supportzeitraum abgelaufen war; die Update-Repositories waren in ein Archiv-Repository verschoben worden, sodass ohne Anpassung des Dockerfiles kein Image mehr gebaut werden konnte
Das ist ein Beispiel dafür, dass unangetastete Software kaputtgeht, und deshalb bevorzuge ich Go und Single Binaries
Wenn man sie als Release paketiert, muss man sie nicht erneut bauen, und in einem Distroless-Docker-Image gibt es außer meinem Binary keine Abhängigkeiten
Ich nutze Go seit Langem, hatte aber nie das Problem, dass Software mit dem Alter kaputtgeht; viele Arten von Problemen, die ich mit Node oder PHP hatte, sind verschwunden
Das zweitgrößte Problem bei Node sind die Indirektionsmuster der Frameworks, das größte ist das Paketmanagement
Peer-Dependency-Probleme wie „Ich habe Version X installiert, aber Modul Y braucht Version Z“ tauchen ständig auf
Viele Engineers prüfen auf GitHub beim Suchen nach Libraries den Zeitpunkt des letzten Commits
Sie neigen dazu, eine Library mit jüngeren Commits für besser unterstützt zu halten
Aber ein archiviertes Projekt, das genau das tut, was man braucht, 0 Bugs hat und seit Jahren stabil ist, ist wie ein verstecktes Juwel im Secondhandladen
Heutzutage verwerfen viele Engineers Libraries automatisch, wenn sie nicht „ständig“ aktualisiert werden, und scheinen das für etwas Gutes zu halten
Moderne Softwareentwicklungsumgebungen sind das oft nicht, und Web-Frontends sind ein typisches Beispiel für häufige Änderungen
Bei einer vollständig eigenständigen Library können ausbleibende Updates in Ordnung sein, aber eine Library, die von einem Web-Frontend-Framework abhängt, verursacht Probleme, wenn sie nicht an Veränderungen im Ökosystem angepasst wird
Die genauen Zahlen kenne ich nicht, aber in der überwältigenden Mehrheit der Fälle bedeutet fehlende aktuelle Aktivität meiner Ansicht nach nicht „fertig und bugfrei“, sondern „aufgegeben“
Manche Sprachen sahen kaum noch wie Version 1.0 aus, andere hatten den Großteil des geschriebenen Codes beibehalten und nur oben drauf ergänzt
Am Ende scheint sich diese Haltung auch in Community und Ökosystem widerzuspiegeln
Clojure war, soweit ich mich erinnere, weit oben auf der Liste, weil es kaum Breaking Changes macht; eine Library, die zuletzt vor fünf Jahren geändert wurde, läuft auch mit der aktuellen Sprachversion einwandfrei
Dass es zur Lisp-Familie gehört und man den Sprachkern ohne Upstream-Änderungen erweitern kann, hilft vermutlich ebenfalls, auch wenn das natürlich seine eigenen Macken hat
Trotzdem fand ich es gut, dass es mir den Gedanken ausgetrieben hat, „frisch“ sei gleichbedeutend mit „gut“
Heute nutze ich häufiger Libraries, die sich seit einigen Jahren kaum verändert haben, als Libraries, die erst letztes Jahr neu entstanden sind, und habe damit keine großen Probleme
Manche Sprachen erscheinen alle ein bis zwei Jahre und fügen neue, elegante Syntax oder abstrakte Datentypen in der Standardbibliothek hinzu, die häufig genutzte, aber unbeholfene Muster ersetzen
Die Community dieser Sprache betrachtet die neue Syntax fast sofort als „idiomatisch“ und findet, dass Code, der noch auf die alten, schwerfälligen Arten geschrieben ist, überarbeitet werden sollte
Der Grund, eine bestimmte Codebasis zu ändern, ist meist, dass die bisherige Vorgehensweise im Vergleich zur neuen Syntax weniger transparent ist und Wartung sowie Code-Reviews erschwert
Die Logik lautet: Hätte es die neue Syntax von Anfang an gegeben, hätte niemand die alte Vorgehensweise für guten Code gehalten; also sollte man den Code aktualisieren, um die Lesbarkeit für neue Entwickler und die Einstiegshürde für Beiträge zu senken
Wenn eine in einer solchen Sprache implementierte Library seit mehr als 3 Jahren nicht aktualisiert wurde, ist das oft ein schlechtes Zeichen
Es kann bedeuten, dass die Entwickler nicht mehr ausreichend mit der Community verbunden sind, um sie in idiomatischem Code zu pflegen, der für andere Entwickler, die die aktuelle Form der Sprache gelernt haben, leicht lesbar ist, und dass sie womöglich auch kein Interesse an externen PRs haben
Es könnte schlicht niemand etwas melden, weil das Projekt als aufgegeben gilt, und es könnten Sicherheitslücken vorhanden sein
Software, mit der man ohne Updates auskommen kann, ist nur Software, die von Anfang an richtig gebaut wurde
Bei Software nur für sich selbst ist das vergleichsweise einfach: Die eigenen Vorlieben ändern sich wahrscheinlich auch nach 10 Jahren nicht stark, und weil
nklein ist, kann man bei kleinen Problemen ignorieren, ob es eineO(n)-Funktion gibt, und stattdessen eineO(n^2)-Funktion verwendenBei Software, die andere nutzen sollen, sind die Anforderungen jedoch anders, und bei ausreichend großem
Nentstehen Probleme, weil eineO(n)-Funktion wertvoll wirdOb man sie für sich selbst oder für andere schreibt: Es können unvorhergesehene Probleme auftreten
Zum Beispiel kann die Software beim Verarbeiten von Dateien über 1 GB abstürzen, aber weil man normalerweise nur Dateien unter 100 KB verwendet, hat man sich nie darum gekümmert; wenn man es dann beheben will, muss man vielleicht die Hälfte neu schreiben
Genau darin liegt der stärkste Einwand gegen die Vorstellung, unveränderliche Software sei grundsätzlich besser als Software, die sich häufig ändert
Unveränderte Software kann von Anfang an perfekt gewesen sein, aber es kann auch tief im Inneren ein Schrecken lauern, und das lässt sich im Voraus schwer unterscheiden
Das heißt auch nicht, dass schnell aktualisierte Software grundsätzlich besser ist als langsam aktualisierte; neben der Update-Geschwindigkeit gibt es viele weitere Faktoren
Wenn sich Anforderungen ändern, muss sich natürlich auch die Software ändern
Aber in 10 Jahren kann vieles passieren, das nichts mit geänderten Anforderungen zu tun hat
Open-Source-Projekte werden aufgegeben oder ändern ihre Richtung, kommerzielle Software wird eingestellt, Firmen werden übernommen, Regeln im App Store oder Play Store ändern sich, APIs verschwinden oder ihre Preise ändern sich, sodass die Wirtschaftlichkeit eines Projekts zusammenbricht
Auch Toolchains, Frameworks, Programmiersprachen, Paradigmen und Best Practices ändern sich
Der Kern ist meiner Ansicht nach, zu verhindern, dass externe Veränderungen, die nichts mit den Anforderungen zu tun haben, mir Änderungen aufzwingen
Das ist ein gutes Prinzip, aber wie immer gibt es Kompromisse
Stabil zu sein ist etwas anderes als veraltet zu sein, und die Grenze verläuft oft bei der Sicherheit
Was tut man, wenn eine wichtige neue Anforderung leicht zu erfüllen wäre, man dafür aber eine vendored Library um sieben Hauptversionen anheben müsste und dadurch jede Menge sachfremde Brüche entstünden?
Was tut man, wenn es nicht mehr genug Leute gibt, die mit einem eingefrorenen Toolset vertraut sind, und niemand es lernen will?
Abhängigkeiten sorgfältig und konservativ auszuwählen ist gut, aber selbst mit den so klein gehaltenen Änderungen an Abhängigkeiten nicht Schritt zu halten, geht meiner Meinung nach einen Schritt zu weit
Die Stimmung des Textes kann ich gut nachvollziehen
Ich hasse es wirklich, dass selbst eine Mobile-App, die erst vor ein paar Jahren gebaut wurde, inzwischen Dutzende Stunden braucht, nur um sie zu patchen und ein Update einzureichen
Interessant ist auch der letzte Teil, in dem der Autor seinen statischen Site-Generator kaltblütige Software nennt und sagt, er laufe unter Python 2
Python 2 wird heutzutage immer schwieriger zu installieren, und am Ende wird auch dieses Projekt ein warmblütiges Projekt werden
Jedes Mal, wenn ich Xcode aktualisiere, muss ich Kleinigkeiten reparieren, damit das Projekt sauber kompiliert und funktioniert; das ist extrem nervig und sollte eigentlich völlig inakzeptabel sein
Die jüngsten git-Commit-Messages sind alle Varianten von „für das neueste Xcode lauffähig gemacht“
Wenn solche Änderungen an untergeordneten SDKs oder am OS wegen Sicherheitsbedrohungen nötig wären, könnte ich es bis zu einem gewissen Grad verstehen, aber das ist fast nie der Fall
Meistens sind es dumme Änderungen wie APIs zu deprecaten, Standardwarnungen hinzuzufügen oder nun dieses Framework statt jenes Frameworks verwenden zu sollen
Plattformen und Frameworks müssen aufhören, absichtlich bewegliche Ziele zu sein, besonders wenn es sich inzwischen um sehr stabile und zuverlässige Betriebssysteme handelt
Man sollte ein 10 Jahre altes Projekt aus dem Gefrierschrank holen können und es sollte genauso sauber kompilieren und laufen wie vor 10 Jahren
Diese Betriebssystemanbieter sind Billionenunternehmen, also will ich keine Ausreden hören, dass Abwärtskompatibilität viel Engineering-Aufwand erfordert
Ich pflege weiterhin ein persönliches Nebenprojekt
Es begann vor 12 bis 13 Jahren in reinem PHP, später schrieb ich es mit Laravel neu und um 2017 herum noch einmal mit Symfony
Weil ich als Freelancer Vollzeit arbeitete und keine Energie hatte, gab es auch Phasen von 6 bis 18 Monaten, in denen ich nur zwei oder drei sehr kleine Commits machte; wenn ich Zeit hatte, fügte ich Funktionen hinzu, aktualisierte, experimentierte und lernte
Es war sehr nützlich, um zu lernen, wie man ein Projekt langfristig pflegt
Ich lernte Dinge wie Dependency-Updates, Unnötiges zu entfernen, Sicherheitsupdates zu prüfen und Gelegenheiten zur Vereinfachung zu finden (von Vagrant zu Docker, von Vue + Axios + Webpack usw. zu Htmx), und ich lernte auch, was man vermeiden sollte
Persönlich meide ich inzwischen frisch entwickelte Abhängigkeiten, Microservices und komplexe Infrastruktur wie Kubernetes
Zuletzt habe ich mehrere Funktionen gebaut, auf PHP 8.2 und Symfony 7 aktualisiert und auch ChatGPT-basierte Funktionen integriert, sodass ich wohl 1 bis 3 Jahre pausieren könnte, wenn ich wollte
In den letzten 4 bis 5 Jahren hat dieses Projekt ungefähr so viel Umsatz gemacht wie ein durchschnittliches Jahreseinkommen als Freelancer, es ist also auch kein schlafendes, unbekanntes Nebenprojekt
Als ich nach ein paar Jahren zurückkam, waren dieselben furchtbaren Bildbearbeitungsfunktionen noch da wie vor 8 Jahren, als ich gegangen war
Neben dem, was im Artikel gesagt wird, ist auch ein inhärent sicheres Threat Model wichtig
Eine vollständige Website zum Beispiel muss sich ständig mit Angreifern und Spam-Bots auseinandersetzen und ist daher im Kern eher warmblütig
Statische Seiten wie TiddlyWiki hingegen muss man nicht einmal ins Web stellen, und der Browser ist eine enorm stabile Plattform, also ist das deutlich besser
Der Unterschied in der Präferenz für kaltblütige und warmblütige Projekte dürfte mit dem Buxton Index aus https://www.cs.utexas.edu/users/EWD/transcriptions/EWD11xx/EWD1175.html zusammenhängen
Ein kleiner Lebensmittelladen in der Nachbarschaft liegt bei etwa 0,5 Jahren, ein wahrer Christ bei unendlich, ein durchschnittlicher Politiker, der wiedergewählt werden will, bei etwa 4 Jahren, die meisten Industrien etwas darüber, und Manager, die Quartalsberichte schreiben müssen, deutlich darunter.
Wichtig ist der Buxton Index, weil enge Zusammenarbeit zwischen Akteuren mit sehr unterschiedlichen Buxton Indizes zwangsläufig scheitert und in moralischen Vorwürfen endet.
Die Seite mit dem kürzeren Horizont wird als oberflächlich und kurzsichtig kritisiert, die mit dem längeren als pflichtvergessen, verantwortungsscheu oder als Trittbrettfahrer.
Am Ende hält man sich gegenseitig auch für dumm.
Der Vorteil des Buxton Index liegt darin, dass er als einfaches Zahlenkonzept moralisch neutral ist und Unterschiede über moralische Debatten hebt.
Das ist besonders wichtig, wenn man über Zusammenarbeit zwischen Wissenschaft und Industrie nachdenkt.
Dieser Name ist wirklich schlecht.
Kaltblüter sind stark von ihrer Umgebung abhängig, während Warmblüter durch ihren Stoffwechsel die Abhängigkeit von der Außentemperatur verringern.
So oder so ist das unnötig mehrdeutig.
Man könnte einfach „Software ohne externe Abhängigkeiten“ sagen und sich den langen erklärenden Absatz sparen.
Ich mag schon Softwareentwicklungsartikel nicht, die mit unpassenden Metaphern aus der Natur zu oberflächlichen Schlussfolgerungen springen; noch weniger mag ich solche Artikel, wenn sie dabei das natürliche Phänomen, auf das sich die Metapher bezieht, völlig falsch verstehen.
Dass einige Arten, darunter die Zierschildkröte, das Einfrieren überleben, liegt nicht an Kaltblütigkeit, sondern an speziellen Frostschutzproteinen.
Andere Eidechsen oder Kaltblüter würden beim Auftauen ihre eigenen Gewebe zerreißen.
https://lobste.rs/s/hitos3/cold_blooded_software#c_mxjzwh