2 Punkte von GN⁺ 2026-06-12 | 1 Kommentare | Auf WhatsApp teilen
  • Der Grund, warum die Popularität statischer Typisierung von den 2000er Jahren bis in die frühen 2010er zurückging und in der zweiten Hälfte der 2010er wieder anstieg, ist nicht ein Trend, sondern die Qualitätsverbesserung statischer Typsysteme
  • So wie man beim Ausheben einer Grube mit bloßen Händen besser dran ist als mit einer Schaufel aus Papier, ist ein dynamisches Typsystem besser als ein miserables statisches Typsystem
  • Frühere statische Typsysteme wie frühes Java oder C++98 konnten nicht einmal zwischen nullable und non-nullable unterscheiden, hatten keine Sum Types, und man musste Typnamen überall ausschreiben
  • Moderne Typsysteme wie TypeScript, Haskell, Swift und Rust bieten Null-Unterscheidung, Sum Types/Union Types und Type Inference standardmäßig
  • Durch die allgemeine Verbreitung von Methoden-Autovervollständigung in IDEs bringen die in statische Typen eingegebenen Informationen neben der Fehlerprüfung auch zusätzliche Produktivitätsgewinne

Hypothese zum Wandel der Beliebtheit statischer Typisierung

  • Dass statische Typisierung in den 2000ern bis frühen 2010ern an Beliebtheit verlor und in der zweiten Hälfte der 2010er wieder zulegte, liegt
    nicht daran, dass Programmierung eine von Trends getriebene Branche ist, sondern daran, dass sich die Qualität der weit verbreiteten statischen Typsysteme verbessert hat

Die Schaufel-Metapher — die Qualität des Werkzeugs entscheidet über die Wahl

  • Wenn eine Schaufel zum Graben einer Grube brauchbar ist, nimmt man natürlich lieber eine Schaufel als bloße Hände,
    aber wenn die einzige verfügbare Schaufel aus Papier gemacht ist, rührt man die Erde nur sinnlos um und ist mit bloßen Händen besser dran
  • In einem dynamischen Typsystem muss man Zustand und Inhalt von Variablen und Feldern vollständig im eigenen Kopf nachverfolgen;
    der Computer hilft nicht, steht aber auch nicht im Weg — das entspricht dem Graben mit bloßen Händen
  • Schwache statische Typsysteme, wie sie in den 1990ern und frühen 2000ern verbreitet waren, sind wie Papierschaufeln
    • Sie helfen nicht einmal bei einfachen Dingen wie der Unterscheidung zwischen nullable und non-nullable Pointern
    • Es gibt nur Product Types, aber keine Sum Types
    • Man hat die Last, Typnamen an allen möglichen Stellen manuell hinschreiben zu müssen
    • Code wie BufferedReader bufferedReader = new BufferedReader(new FileReader(filename)); ist eine kleine Katastrophe

Was moderne Typsysteme heute immer bieten

  • Im Vergleich zu modernen Typsystemen wie TypeScript, Haskell, MyPy, Swift und Rust bieten sie heute immer Folgendes
  • Unterscheidung zwischen nullable und non-nullable

    • Haskell bietet Maybe t, TypeScript T | null, Swift T?, Rust Optional<T>
    • Das Typsystem zeigt leicht alle Stellen, an denen ein Null-Check nötig ist, sowie Auslassungen an
    • Tatsächlich sieht man Laufzeitfehler durch Null Pointer fast nie mehr
  • Sum Types oder Union Types

    • Sie ermöglichen die Praxis „Make invalid states unrepresentable“
    • Man kann State-Machine-Objekte mit mehreren Feldern erstellen, wobei jedes Feld nur dann existiert, wenn das System sich im betreffenden Zustand befindet
  • Type Inference

    • Der Compiler kann let x = 5; als Zahl erkennen, daher muss man nicht let x: number = 5; schreiben

Zusätzlicher Nutzen durch die Verbreitung von IDE-Funktionen

  • Die allgemeine Verbreitung von IDE-Funktionen wie der automatischen Vervollständigung von Methodennamen hat den Nutzen statischer Typsysteme erhöht
    • In den 1990ern war Intellisense in Visual Studio eine Killerfunktion, aber in den 2020ern gibt es Ähnliches in fast jeder IDE und jedem Editor
    • Die Informationen, die man in ein statisches Typsystem steckt, führen nicht nur zur Fehlerprüfung, sondern auch zu zusätzlichen Produktivitätsvorteilen

Fazit

  • Ein gutes dynamisches Typsystem ist besser als ein schlechtes statisches Typsystem
  • Heute verfügen wir jedoch über deutlich bessere statische Typsysteme als früher

1 Kommentare

 
GN⁺ 2026-06-12
Lobste.rs-Kommentare
  • Der Artikel ist gut, aber ich stimme ihm nicht vollständig zu. Die statischen Typsysteme der frühen 2000er waren zwar nicht großartig, aber meiner Meinung nach immer noch viel besser als gar keine statischen Typen.
    Geschlossene Summentypen gab es nicht, aber mit Subtyping ließ sich ein erheblicher Teil modellieren, und nicht-nullbare Typen gab es zwar ebenfalls nicht, doch C++-Referenzen und Nicht-Pointer-Typen sowie Javas primitive Typen deckten einen Teil davon ab. In Ruby oder JavaScript waren dagegen nicht nur alle Typen nullable, sie konnten außerdem wie Strings, wie Integers oder wie jeder andere Typ im Programm behandelt werden, was die Lage noch schlechter machte.
    Ein großer Grund für den Stimmungswandel bei statischen Typen war meiner Ansicht nach, dass während des Web-2.0-Social-Network-Booms der First-Mover-Vorteil wichtiger war als alles andere. Selbst wenn man mit Ruby oder Python technische Schulden anhäufte, war schnelles Launchen und Iterieren besser, als wie Friendster oder Digg verdrängt zu werden, und wenn etwas langsam war, konnte man mit dem damals leicht verfügbaren Niedrigzinskapital einfach mehr Server kaufen.
    Mit dem anschließenden Mobile-Boom lief Software dann auf eingeschränkten Benutzergeräten, die man nicht kontrollieren konnte, und langsame dynamisch typisierte Apps waren einfach tatsächlich langsam; außerdem ließen sich Typfehler nicht wie auf dem Server elegant über einen Top-Level-Response-Handler abfangen und beheben. In dieser Umgebung wirkten Sicherheit und Performance statischer Typen deutlich überzeugender.

    • Es gibt einige Arbeiten, die Java und C++ im Stil der 90er mit Codebasen in dynamisch typisierten Sprachen vergleichen und zu ähnlichen Bugraten kommen; Anhänger dynamischer Sprachen führen das oft als Beleg an, dass statische Typen nicht nützlich seien.
      Anfang der 2000er hätte ich dem noch zugestimmt, denn die damaligen Typsysteme erzwangen oft nur Eigenschaften, die ohnehin fast nie falsch waren, und legten dabei Einschränkungen auf, die beim Strukturieren von Code nicht halfen. Besonders die Kopplung von Subtyping und Implementierungsvererbung war unflexibel.
      Mit moderneren Typsystemen habe ich meine Meinung geändert. In snmalloc erzwingt das C++-Typsystem eine Zustandsmaschine für Speicherbesitz, und in anderen Codebasen prüft es das korrekte Overflow-Verhalten von Ringpuffer-Zählern. Beides sind häufige Fehlerquellen, deren Debugging lästig ist, wenn sie falsch sind; und tatsächlich hat der Compiler bei Code, den ich für korrekt hielt, Fehler gemeldet und so verhindert, dass Bugs überhaupt in den Tree gelangen.
    • Ich halte Entwicklung in dynamisch typisierten Sprachen für langsamer als in statisch typisierten. Ich sehe ständig die gegenteilige Behauptung, kann sie aber nicht nachvollziehen.
      Wenn man im IDE . drückt, ein Stück des Methodennamens eintippt und dann beim richtigen Vorschlag Enter drückt, spart man alle paar Sekunden 2 Sekunden; und wenn man nicht weiß, welche Methoden es gibt, spart man auch die 30 Sekunden, die sonst für das Nachschlagen der Klassendefinition draufgehen. Das Prinzip ist auch unter https://grugbrain.dev/#grug-on-type-systems gut beschrieben.
      Weil man Zeilen mit Methodenaufrufen viel häufiger schreibt als Typannotationen für Funktionsparameter, fällt dieser Trade-off überwältigend zu Ungunsten dynamischer Typisierung aus. Der wirklich wertvolle Teil war nie, zur Laufzeit scheiternden Unsinnscode zu erlauben, sondern lokale Variablentypen wegzulassen, und statisch typisierte Sprachen mussten das von vornherein gar nicht verbieten.
    • Die populären Typsysteme der frühen 2000er waren nicht nur „nicht großartig“, sondern schlecht und extrem geschwätzig.
      In den seltenen Codebasen, die Typsysteme ernsthaft nutzten, stapelten sich seitenweise Code ohne Aussagekraft, dazu kamen trotzdem Berge von Laufzeit-if-Abfragen, und in Java wurde das Programm mit wachsender Typhierarchie in der Praxis auch noch langsamer. Die meisten Codebasen nutzten Typen nur lückenhaft und bauten viele Laufzeitprüfungen ein, sodass der nötige Testumfang gegenüber dynamischen Typsystemen nicht wesentlich kleiner war.
      Dynamisch typisierte Sprachen boten keine statischen Vorteile, waren dafür aber kompakt, leichter zu lesen und zu reviewen und auch leichter zu testen. Das galt besonders in Umgebungen wie den Dependency-Injection-Frameworks der späten 90er und frühen 2000er, in denen man für jeden neuen Service mehrere XML-Dateien anfassen musste. Und man konnte arbeiten, ohne ein IDE zu brauchen, das die Hälfte des RAM fraß.
      Genau so sah mein früher Berufsalltag aus, deshalb stimme ich dem Artikel weitgehend zu. Das Verhältnis von Kosten zu Nutzen bei Java 1.4 bis Java 6 war so schlecht, dass ich statisch typisierte Sprachen fast ganz aufgegeben hätte. Erst als ich einige Jahre später hobbymäßig mit Haskell spielte, wurde mir klar, dass auch statische Typen ein vernünftiges Kosten-Nutzen-Verhältnis haben können und das Problem Java war. Der Essay „python is not java“ zeigt diese dunkle Zeit ebenfalls gut.
    • Vererbungsbasiertes Subtyping war sogar noch schwächer. Man bekam nicht die Benutzerfreundlichkeit von Pattern Matching mit Vollständigkeitsprüfung, und die Implementierung war auf viele Stellen verteilt.
    • Die Erklärung, dass es wichtig war, vor der Konkurrenz eine Website online zu bringen, sie den Nutzern vorzusetzen und Skaleneffekte früh festzuschreiben, klingt auch für die heutige Situation ziemlich vertraut.
  • Ich bezweifle, dass wir nach dem Aufstieg statischer Typen zum Zeitgeist den Zuverlässigkeitsgewinn in unserer Software wirklich gesehen haben.
    Ich dachte immer, der Vorteil statischer Typen liege viel stärker im unmittelbaren Feedback während der Entwicklung und in der Verringerung schwerwiegender Laufzeitfehler; theoretisch sind solche Fehler zwar immer möglich, aber in der Praxis schienen sie mir nie so häufig vorzukommen.

    • Doch, habe ich. Als wir in einer nicht ganz kleinen TypeScript-Codebasis anfingen, 0 TypeScript-Fehler als Ziel zu setzen, gingen Versuche, Methoden auf undefined und null aufzurufen, stark zurück.
      Junioren und einige Senioren waren anfangs skeptisch und meinten, überall würden @ts-ignore auftauchen, aber tatsächlich waren es nur etwa drei, einschließlich solcher, die durch kaputte Typen in Abhängigkeiten verursacht wurden. Früher ist mir etwa einmal pro Woche im Entwicklungs-Branch die App wegen Typverwechslungen abgestürzt und hat meine Arbeit blockiert; heute kann ich mich nicht einmal erinnern, wann das zuletzt passiert ist.
      Schon allein tsc zufriedenzustellen reduziert typbezogene Bugs, selbst wenn ich den Code nicht selbst geschrieben habe. Andererseits sind Linter heute übermäßig eifrig, und beim Versuch, Tools wie Sonar zufriedenzustellen, habe ich echte Refactoring-Schäden gesehen. 95 % der Warnungen waren falsch, 3 % waren Bugs im Tool, und selbst die nützlichen 2 % waren nicht die eigentliche Ursache realer Bugs. Ich habe eine Woche damit verbracht, die Codebasis anzupassen, einen Bug gefixt und dabei im Prozess zwei weitere eingebaut.
      Die Arbeit, tsc zufriedenzustellen, erzeugte grob geschätzt pro Tag zwei reine Bugfixes und eine Regression, aber die Regressionen waren meist weniger schwerwiegend als ein kompletter Crash und eher falsches Verhalten.
      Wenn man dazu noch Property-based Testing nimmt, dauerte das im Schnitt 2–4 Stunden und deckte immer mindestens einen Bug auf. Wenn der Code sich für Property-based Testing eignet, sollte man es tun.
      Mit dem günstigen Modell DeepSeek V4 Flash haben wir den Testumfang ausgeweitet und dabei darauf geachtet, keinen Testmüll zu erzeugen; so haben wir pro Tag ungefähr 2–3 Logikfehler behoben, ohne Crashes. Das Testpaket ist allerdings nur gerade so wartbar.
      Als wir Junioren mit Sonnet und Opus 4.5 bzw. 4.6 grob Tests erstellen ließen, erzeugten die Modelle nur Tests, die das „aktuelle Verhalten dokumentieren“, sodass die Wirkung gering war, und das Testpaket war so unwartbar, dass es verworfen werden musste.
      Modellbasiertes Testen ist sehr gut darin, Bugs zu finden, aber die Einrichtung ist kompliziert, und es ist extrem mühsam, die Modelle dazu zu bringen, in alle Ecken vorzudringen, statt nur Zyklen auf oberflächliche Funktionen zu verschwenden. So etwas wie ein profilbasiertes modellbasiertes Fuzzer-Tool fände ich interessant.
      Kurz gesagt: Typprüfer fangen schwere Fehler und viele Verwechslungen gut ab, und Property-based Testing ist hervorragend. Gewöhnliche Tests erfordern viel Disziplin, wenn man dauerhaft Nutzen daraus ziehen will.
    • Ich persönlich würde sagen: ja. In dem JavaScript, das ich benutze, sind Null-Pointer-Bugs seit dem Umstieg auf TypeScript fast vernachlässigbar geworden, und bei meinen Kollegen war es ähnlich.
  • Am wenigsten kann ich zustimmen, wenn TypeScript hier mit einem guten Typsystem gleichgesetzt wird.

    • Stimmt. TypeScript ist nicht sound, und besonders die Art, wie es Typen über await hinweg verengt, hat mich mehrfach gebissen. Trotzdem hat es die Lage dramatisch verbessert.
      Ehrlich gesagt habe ich mich am strukturellen Typsystem am Ende auch gewöhnt, und ich denke, es wird sich positiv auf künftiges Sprachdesign auswirken.
  • Diese Behauptung ist wenig überzeugend. Ordentliche Programmiersprachen mit algebraischen Datentypen und Typinferenz gab es schon seit Mitte der 90er.
    Die Typsysteme von Java und C++ waren sehr dürftig, aber SML, OCaml und Haskell gab es bereits, und sie fühlten sich dem heutigen Stand gar nicht so unähnlich an. Wenn die Leute diese Sprachen nicht benutzt haben, dann ist das eine Frage von Kultur, Akzeptanz und unausgesprochenen Anforderungen, nicht etwas, das sich allein damit erklären ließe, dass „benutzbare Typsysteme nicht gut genug waren“.
    Oder falls die Behauptung lautet: „Die Typsysteme der damals populären Sprachen waren schlecht, und die Typsysteme der heute populären Sprachen sind besser, deshalb sind Typsysteme populärer geworden“, dann klingt das wie ein Zirkelschluss.
    Auch beim Unterschied zwischen Sprachen, die zusammen mit einem Typsystem entworfen wurden, und Sprachen, die ursprünglich ohne Typen entworfen und später mit einem Typsystem versehen wurden, steckt viel Nuance drin.

  • Aus der Perspektive von jemandem, der ursprünglich dynamische Typisierung bevorzugt hat, finde ich den Beitrag ziemlich fair. Heute arbeite ich mit C#; als Hobby nutze ich Lisp, und früher habe ich auch Python verwendet.
    Als ich Java 5 verwenden musste, habe ich ständig mit dem Typsystem gekämpft, meist wegen schlechter Entscheidungen von Bibliotheksentwicklern. Nach dem Wechsel zu C# um 2010 war das Typsystem nicht aktiv schädlich, aber überwiegend redundant und konnte auch die in Python häufigste Typverwechslung, nämlich Null-Pointer-Exceptions, nicht verhindern.
    Wirklich hilfreich wurde das C#-Typsystem erst etwa 2020 mit den nicht-nullbaren Referenztypen. Dieses Jahr kommen auch native Union-Typen dazu, aber Bibliotheken für Union-Typen mit erzwungener Vollständigkeit waren mindestens seit 2016 möglich, und ich benutze sie seit 2020.
    Ich denke, Mode spielt weiterhin eine Rolle, aber ein Teil davon ist nicht schlecht. Trendsprachen mit ausdrucksstärkeren Typsystemen haben auch Verbesserungen in die gewöhnlichen Sprachen gebracht, mit denen wir unser Geld verdienen.

  • Haskell und sein Typsystem gab es schon in den 2000ern. Es wurde damals nicht so breit genutzt wie heute, aber es existierte eindeutig, daher sollte diese Behauptung in diesem Punkt ergänzt werden.
    Ich persönlich denke, dass TypeScript ein großer Faktor dabei war, Nutzer von Mainstream-Sprachen mit besseren Typsystemen vertraut zu machen. Neben der Qualität und der Unterstützung durch Microsoft hatte es den Vorteil, auf JavaScript angewendet zu werden, und JavaScript brauchte Typen dringender als Python. Wegen „Undefined is not a function.“ und „The good parts.“

    • Ich würde mir wünschen, dass ein „good parts“-Buch für die aktuelle JavaScript-Generation erscheint und dabei seine Prägnanz beibehält.
      „Real World Haskell“ erschien 2008 und hatte das Ziel, Haskell für Mainstream-Programmierer attraktiver wirken zu lassen. Ich weiß nicht, wie sehr es dabei geholfen hat, die gute Nachricht zu verbreiten.
      In der Java-Welt brachte Scala 2004 schicke Typen, und für .NET erschien F# 2005. Scala hat vielleicht die meisten auffälligen Nutzer wie Twitter gewonnen, war aber nicht in der Position, einen großen Anteil der Nutzer seiner Plattform aufzusaugen wie TypeScript, und es war auch nicht attraktiv genug, um wie Rust oder Go in großem Stil Nutzer anderer Sprachen anzuziehen.
    • Der Artikel behandelt dieses Problem bereits. Er vergleicht dürftige statische Typsysteme wie frühes Java oder C++98, die in den 90ern und frühen 2000ern populär waren, mit Papierschaufeln.
      Im direkt folgenden Absatz erwähnt er Haskell als „modernes Typsystem“, aber der Anteil der Leute mit Haskell-Erfahrung in den späten 90ern und frühen 2000ern lag praktisch bei 0 %, selbst wenn man rein persönliches Herumprobieren mitzählt. Im Artikel geht es darum, wie die Mehrheit der Entwickler statisch typisierte Sprachen damals erlebt hat und warum diese Mehrheit statisch typisierte Sprachen kollektiv mied.
    • Ich denke, Haskell und OCaml leiden zum Teil unter einem schwachen Tooling-Ökosystem. Die Sprachen selbst sind großartig, aber bei den Tools verlieren sie durch unzählige kleine Ärgernisse an Akzeptanz.
      Um zum Beispiel in OCaml dune zu benutzen, muss man opam-Dateien, dune-Dateien, die ocaml module-Syntax und die ocaml-Syntax verstehen. Die optionalen Compiler-Erweiterungen in Haskell wirken genauso einschüchternd.
      Im Gegensatz dazu muss man bei cargo nur toml und Rust kennen.