Connect with us

Entwicklung & Code

Warum objektive Schätzungen in der Softwareentwicklung nicht funktionieren


Kann man den Aufwand eines Softwareprojekts objektiv schätzen? Solche Zahlen als Ergebnis würden klären, wie lange die Implementierung einer Funktionalität dauert und was sie kostet. Das könnte bei der Beauftragung von Entwicklungsdienstleistungen helfen.

Weiterlesen nach der Anzeige


Eberhard Wolff

Eberhard Wolff

(Bild: 

Eberhard Wolff

)

Eberhard Wolff ist Head of Architecture bei SWAGLab und arbeitet seit mehr als zwanzig Jahren als Architekt und Berater, oft an der Schnittstelle zwischen Business und Technologie. Er ist Autor zahlreicher Artikel und Bücher, u.a. zu Microservices und trägt regelmäßig als Sprecher auf internationalen Konferenzen vor. Sein technologischer Schwerpunkt sind moderne Architektur- und Entwicklungsansätze wie Cloud, Domain-driven Design und Microservices.

Bei agilen Methoden kann der Aufwand einer Funktionalität als Story in Planungsmeetings abgeschätzt werden, um zu entscheiden, ob eine Story implementiert werden soll. Das ist optional: Das Schlagwort NoEstimates beschreibt Projekte ganz ohne Aufwandsschätzungen. Vorteil davon: Der Aufwand für die Schätzungen entfällt und kann in die Entwicklung von Storys fließen. Es reicht aus, wenn alle im Team gerade am wertvollsten Feature arbeiten. Woody Zuill ist ein Pionier auf diesem Gebiet und hat noch nie anders gearbeitet.

Eine Abschätzung von Storys basiert meistens auf einer Referenz-Story oder einer Funktionalität, der die Größe eins zugewiesen wird. Andere Storys werden relativ zu dieser Referenz abgeschätzt, und zwar durch und für das Team, das die Storys abschätzt. Hinzu kommt die Velocity (Geschwindigkeit) des Teams. Sie wird in implementierten Story Points pro Iteration gemessen und kann sich über den Projektverlauf ändern.

Also ist die Schätzung nicht objektiv, weil sie das jeweilige Team einbezieht und sich zudem die Velocity mit der Zeit ändert. Die Schätzungsmethodik erreicht aber das Ziel: Sie erlaubt eine Planung der Arbeit für Teams. Durch die Analogieschätzung zur Referenz-Story und die Velocity ist auch eine relativ gute Schätzung dazu möglich, was das Team leisten kann.

Natürlich kann man versuchen, diese Messungen zu objektivieren. Beispielsweise kann man eine einheitliche Referenz-Story wählen. Es gibt Ansätze, die noch deutlich weitergehen. Das Function-Point-Verfahren versucht, eine objektive Komplexität einer fachlichen Anforderung in Function Points zu messen. Auch diese Verfahren haben aber eine Streuung in der Abschätzung. Außerdem erfordern sie Erfahrung beim Schätzen und sind aufwendig. Verfahren wie COCOMO warnen sogar davor, dass die Ergebnisse nur grobe Richtungen und keine präzisen Schätzungen sind.

Weiterlesen nach der Anzeige

Die Nützlichkeit dieser Abschätzungen ist fragwürdig: Projekte ändern immer den Scope. Wenn Software in der Praxis genutzt wird, ergeben sich neue Wünsche. Softwareentwicklung ist ein Prozess, bei dem Technikerinnen und Nutzer gemeinsam lernen, wie man die Geschäftsprozesse am besten mit Software unterstützt. Wenn man etwas Neues lernt, wird man den Scope ändern. Dann sind präzise Abschätzungen über den ursprünglichen Scope nicht mehr viel wert und der Aufwand für die zusätzliche Präzision ist nicht sinnvoll investiert. Vielleicht ist das der Grund, warum man in der Praxis zwar agiles Schätzen genutzt wird, aber kaum ausgefeiltere Methoden.

Überhaupt ist der Fokus auf die Aufwandsseite bei Projekten oft übertrieben. Wie jedes Investment muss auch ein Investment in Software einen Wert erzeugen, der höher ist als das Investment – idealerweise um ein Vielfaches. Das Abschätzen des Werts scheint jedoch oft gegenüber dem Aufwand stiefmütterlich betrachtet zu werden.

Es gibt sehr kleine Teams, die extreme erfolgreiche Software entwickelt haben. Solche Ausreißer werfen die Frage auf, ob eine objektive Schätzung überhaupt machbar ist. Instagram war vor der Akquisition eine Milliarde Dollar wert, betrieb ein weltweit skaliertes System und das mit nur 13 Mitarbeitern und Mitarbeiterinnen. WhatsApp hatte vor der Akquisition 900 Millionen User; 50 Engineers haben dieses System entwickelt und betrieben. Das ist ein Bruchteil dessen, was viele IT-Projekte in etablierten Unternehmen an Personal hat.

Diese Anwendungen waren scheinbar aber nur eine einfache Foto-Sharing-App bzw. Messaging-App. Erklärt das den niedrigen Personalaufwand? Auch eine Versicherung oder ein Sparvertrag sind einfach: Man zahlt Geld ein und unter bestimmten Bedingungen bekommt man Geld zurück. Suchmaschinen sind auch einfach: Es gibt sogar Software von der Stange, mit der man Dokumente indizieren und durchsuchen kann.

Auf der oberflächlichen Ebene erscheint alles einfach. Die Komplexität des Problems wird erst klar, wenn man die Details betrachtet und das Problem wirklich versteht.

Unabhängig von der Komplexität: Der ökonomische Erfolg eines Projekts ist das eigentlich wichtigste Ergebnis. In diesem Bereich überzeugen Instagram und WhatsApp wirklich: Sie haben Milliarden an Werten geschaffen. Das ist fast nur für erfolgreiche Start-ups möglich. Auf jedes erfolgreiche Start-up kommen zahlreiche, die nicht erfolgreich sind.

Nehmen wir an, ein etabliertes Unternehmen würde eine Foto-Sharing-App oder Messaging-App bauen. Sie würde es mit ihren typischen Mechanismen bauen: potenziell Hundertschaften an Developern, ausgefeiltes Projektmanagement und Projektpläne. 13 Menschen, die ein zentrales System für den Durchbruch am Markt entwickeln, würden eher als ein Risiko wahrgenommen werden. Große Teams und Projekte führen außerdem zu viel Prestige – für alle Beteiligten, für Manager, aber auch Technikerinnen. Und schließlich haben etablierte Unternehmen meist viele Softwareprojekte und meist hängt von keinem das Überleben der Firma ab – anders als bei einem Start-up. Der Druck ist also objektiv geringer.

Es gibt also genügend Mechanismen, die zu einem ganz anderen Aufwand führen. Ein etabliertes Unternehmen wird also ein Softwaresystem mit den Mechanismen entwickeln, die es typischerweise nutzt, und findet sich in einer anderen Wettbewerbssituation als ein Start-up. In einem Start-up sind die wirtschaftlichen Bedingungen hingegen so, dass möglichst schnell irgendwas live gehen muss – mit den entsprechenden Konsequenzen.

Es ist außerdem unwahrscheinlich, dass ein etabliertes Unternehmen genau dieselbe Anwendung für ein Problem wie Foto-Sharing oder Messaging bauen würde. Die IT-Landschaft, in die sich die Anwendungen integrieren müssen, ist anders. Die Anforderungen können erweitert werden, was ein Start-up wegen der wirtschaftlichen Rahmenbedingungen nicht kann.

Ein Start-up würde daher vermutlich auch in Bereichen, die klassisch mit komplexen Softwaresystemen implementiert werden, eine viel einfachere Lösung entwickeln – weil sie eben schnell auf den Markt kommen müssen und ein sehr begrenztes Budget haben.

Den objektiven Aufwand bewerten zu wollen, ist dann noch weniger sinnvoll: Software löst ein Problem, das eine Organisation hat. Beispielsweise will die Organisation ein neues Produkt etablieren. Dazu greift es auf soziale Prozesse zurück, wie sie in der Organisation verankert sind. Für ein Start-up ist das Vorgehen eines etablierten Unternehmens genauso fremdartig, wie das Vorgehen eines Start-ups fremdartig für ein etabliertes Unternehmen ist. Das umfasst nicht nur das Vorgehen bei der Entwicklung, sondern auch das Produkt.

Am Ende sollte uns das auch nicht interessieren. Es geht nur darum, wie wir in der jeweiligen Situation bessere Software entwickeln können. Und dafür gibt es immer zahlreiche, spannende Möglichkeiten.

Objektiv den Aufwand für ein Feature festzustellen, ist vielleicht mit viel Aufwand möglich. Da es jedoch für die Planung unnötig und aufwendig ist, nutzen die meisten Projekte solche Techniken gar nicht erst. Software ist außerdem „nur“ eine Implementierung der Lösung eines Businessproblems. Organisationen gehen Probleme anders an, sodass alleine schon deswegen die Lösung und der Aufwand unterschiedlich sein werden. Dennoch kann und sollte man die Frage nach der Produktivität und einem besseren Vorgehen stellen.


(rme)



Source link

Entwicklung & Code

Nun auch Gmail: Nutzer dürfen bald ihre Mail-Adresse ändern


Im Netz sind Hinweise aufgetaucht, dass nun bald auch Gmail-Nutzer die Mail-Adresse für ihren Google-Account ändern dürfen. Bislang konnten Anwender die Adresse nur wechseln, sofern es sich nicht um eine @gmail.com handelte. Auf einer indischen Supportseite kündigt Google jetzt jedoch an, dass sich das ändern soll.

Weiterlesen nach der Anzeige

Auf der Seite in Hindi heißt es: „Wichtiger Hinweis: Die Funktion zum Ändern der E-Mail-Adresse eines Google-Kontos wird schrittweise für alle Nutzer eingeführt. Daher ist diese Option möglicherweise derzeit für Sie nicht verfügbar.“ (übersetzt mit DeepL). Eine Änderung von einer @gmail.com- ist nur in eine andere @gmail.com-Adresse möglich. Die alte Adresse bleibt als Alias bestehen und kann weiter Nachrichten empfangen sowie für das Login verwendet werden.

Auch bereits vorhandene Daten und Nachrichten bleiben erhalten. Eine Änderung ist einmal im Jahr möglich. Dass das Dokument derzeit nur in Indien erscheint, könnte darauf hindeuten, dass Google die Funktion dort vor einem weltweiten Start testen möchte.


(who)



Source link

Weiterlesen

Entwicklung & Code

Ruby 4.0: Viel Umbau unter der Haube, wenig neue Features


close notice

This article is also available in
English.

It was translated with technical assistance and editorially reviewed before publication.

Am 21. Dezember 2025 wurde die Sprache Ruby 30 Jahre alt – und seit rund 20 Jahren veröffentlicht ihr Schöpfer, Yukihiro Matsumoto (Matz), jedes Jahr zu Weihnachten eine neue größere Version, am 25. Dezember 2025 sogar eine mit der runden Versionsnummer 4.0. Die ist, soviel sei vorweggenommen, eher Dekoration und dem Jubiläum geschuldet als tatsächlich durch zahlreiche neue Features gerechtfertigt. Doch da Ruby ohnehin keiner strengen semantischen Versionierung folgt und größere Breaking Changes meidet wie der Teufel das Weihwasser (zumindest seit Ruby 1.9), ist das legitim.

Weiterlesen nach der Anzeige




Stefan Wintermeyer ist freier Consultant und Trainer. Er beschäftigt sich mit Phoenix Framework, Ruby on Rails, Webperformance und Asterisk.

Andererseits war 2025 ein spannendes Jahr für Ruby und die Ruby-on-Rails-Welt, so dass dieser Artikel neben dem Blick nach vorn auf Ruby 4 auch auf das zurückblickt, was Ruby in den letzten Monaten technisch erreicht hat und die jetzt erschienene Version in diesem Kontext stellt. Denn obwohl das Vorurteil von Ruby als langsamer Sprache schwer auszurotten ist, hat die Sprache durch langjährige, fortdauernde Entwicklung eine beeindruckende Performance entwickelt.

Der mit Ruby 3.4 noch einmal deutlich optimierte Just-in-Time-Compiler YJIT erreicht in Benchmarks von Shopify eine Leistungssteigerung von 92 Prozent gegenüber dem Interpreter. Der Praxisbeweis kam am Black Friday 2025: Shopify wickelte mit seiner Ruby-on-Rails-Infrastruktur Einkäufe von 81 Millionen Kunden ab. Die Spitzenlast betrug 117 Millionen Requests pro Minute auf den Applikationsservern, während die Datenbanken 53 Millionen Lesezugriffe und 2 Millionen Schreibzugriffe pro Sekunde bewältigten.

Doch auch die Arbeiten an künftigen Performance-Optimierungen gehen weiter voran. Die technisch bedeutendste Neuerung in Ruby 4 ist ZJIT, ein experimenteller Method-Based-JIT-Compiler, den das gleiche Team bei Shopify entwickelt wie YJIT. ZJIT wurde im Mai 2025 nach Matz‘ Zustimmung auf der Konferenz RubyKaigi in den Master-Branch gemerged.

ZJIT unterscheidet sich architektonisch fundamental von YJIT. Während YJIT den Bytecode der Ruby-VM YARV direkt in Low-Level-IR kompiliert und dabei einen Basic-Block nach dem anderen verarbeitet (Lazy Basic Block Versioning), verwendet ZJIT die Static Single Assignment Form (SSA) als High-Level Intermediate Representation (HIR) und kompiliert komplette Methoden auf einmal. Diese Architektur soll breiteren Community-Beiträgen den Weg ebnen und langfristig die Speicherung kompilierten Codes zwischen Programmausführungen ermöglichen.

Der Name ZJIT hat übrigens keine bestimmte Bedeutung, sondern steht einfach für den Nachfolger von YJIT. Intern wird ZJIT als der „wissenschaftliche Nachfolger“ bezeichnet, da die Architektur klassischen Compiler-Lehrbüchern entspricht und damit leichter zu verstehen und zu erweitern ist. Der Compiler ist als experimentell eingestuft und bringt derzeit in produktiven Projekten noch keine Vorteile. Wer sich damit beschäftigen will, muss Ruby mit der Configure-Option --enable-zjit neu bauen und bei der Ausführung Ruby mit der Option --zjit aufrufen.

Weiterlesen nach der Anzeige

Seit Ruby 3.4 gibt es mit it einen eleganten impliziten Block-Parameter für Einzeiler. Er ist lesbarer als die nummerierten Parameter (_1, _2), die seit Ruby 2.7 existieren, und spart die explizite Parameterdeklaration. Die klassische Deklaration mit explizitem Parameter


users.map { |user| user.name }


und mit impliziten nummeriertem Parameter


users.map { _1.name }


wird also ergänzt durch


users.map { it.name }


Besonders intuitiv und praktisch ist das beim Method Chaining:


files
  .select { it.size > 1024 }
  .map { it.basename }
  .sort { it.downcase }


Der Bezeichner it liest sich wie natürliche Sprache und macht den Code selbstdokumentierend. Wichtig: it funktioniert nur in Blöcken mit genau einem Parameter. Bei mehreren Parametern bleiben _1, _2 oder explizite Namen die richtige Wahl.

Der Splat-Operator (*) entpackt Arrays in einzelne Elemente – etwa um die Elemente aus [1, 2, 3] als drei separate Argumente an eine Methode zu übergeben. Ab Ruby 4.0 ruft der Ausdruck *nil nun nicht mehr nil.to_a auf, sondern liefert direkt ein leeres Array. Das entspricht dem Verhalten des Double-Splat-Operators (**) für Hashes, bei dem **nil bereits seit längerem nil.to_hash nicht mehr aufruft. Diese Vereinheitlichung macht das Verhalten konsistenter und weniger überraschend. Das zeigt sich zum Beispiel, wenn man optionale Elemente, zum Beispiel aus einer Datenbank-Abfrage, in ein Array einfügen will:


optional_tags = nil


Mit Ruby 4.0 funktioniert das sauber – *nil wird zu nichts und muss nicht explizit abgefangen werden:


post = { title: "Ruby 4.0", tags: ["news", *optional_tags, "ruby"] }
#=> { title: "Ruby 4.0", tags: ["news", "ruby"] }


Die binären logischen Operatoren ||, &&, and und or am Zeilenanfang setzen nun die vorherige Zeile fort – analog zum Fluent-Dot-Stil bei Methodenketten. Das ermöglicht elegantere Formatierung von Bedingungen, analog zum Method Chaining:


result = first_condition
   second_condition
  && third_condition


Diese Änderung erlaubt bessere Lesbarkeit bei längeren logischen Ausdrücken, ohne Backslashes oder Klammern zur Zeilenfortsetzung verwenden zu müssen.

Ractors sind Rubys Antwort auf das Problem der echten Parallelität. Anders als Threads, die durch den Global VM Lock (GVL) serialisiert werden, können Ractors tatsächlich parallel auf mehreren CPU-Kernen laufen. Der Name ist ein Kofferwort aus Ruby und Actor – das Konzept basiert auf dem Actor-Modell, bei dem isolierte Einheiten ausschließlich über Nachrichten kommunizieren. Ractors gelten auch in Ruby 4.0 noch als experimentell. Der IRB zeigt eine entsprechende Warnung an.

Der GVL war lange Zeit Rubys größte Schwäche bei CPU-intensiven Aufgaben. Zwar konnten Threads I/O-Operationen parallelisieren, da der Lock bei I/O freigegeben wird, aber Berechnungen liefen immer sequentiell. Ractors umgehen dieses Problem, da sie sich keinen gemeinsamen GVL mehr teilen; jeder Ractor führt den Code unabhängig aus. Ruby synchronisiert intern nur noch an spezifischen Punkten.

Jeder Ractor besitzt seinen eigenen Speicherbereich. Objekte können nicht zwischen Ractors geteilt werden – außer sie sind unveränderlich. Diese strikte Isolation eliminiert Race Conditions by Design (siehe Listing 1):


$ irb
irb(main):001> r = Ractor.new { 2 + 2 }
(irb):1: warning: Ractor is experimental, and the behavior may change in future versions of Ruby! Also there are many implementation issues.
=> #
irb(main):002> r.join
=> #
irb(main):003> puts r.value
4
=> nil


Das Beispiel zeigt den typischen Ractor-Lebenszyklus: Ractor.new startet einen neuen Ractor mit dem übergebenen Block, join wartet auf dessen Beendigung, und value liefert das Ergebnis – hier die berechnete Summe 4. Bei einer so simplen Berechnung wie 2 + 2 ist der Ractor bereits beendet (terminated), bevor der Aufruf von join erfolgt. Der Vollständigkeit halber zeigt das Beispiel trotzdem den kompletten Ablauf – bei längeren Berechnungen ist join essenziell, um auf das Ergebnis zu warten.


irb(main):004> def fib(n) = n < 2 ? n : fib(n-1) + fib(n-2)
irb(main):005* ractors = [35, 36, 37, 38].map do |n|
irb(main):006*   Ractor.new(n) { fib(it) }
irb(main):007> end
=> 
[#,
...
irb(main):008> results = ractors.map(&:value)
=> [9227465, 14930352, 24157817, 39088169]


Ractors zeigen ihre Stärke bei CPU-intensiven Aufgaben. Das Beispiel in Listing 2 demonstriert das. Es berechnet mittelgroße Fibonacci-Zahlen parallel. Auf einem Vier-Kern-System läuft dieses Beispiel nahezu viermal so schnell wie die sequentielle Variante. Im Tarai-Benchmark – einem klassischen Rekursions-Test – erreichen vier parallele Ractors eine 3,87-fache Beschleunigung gegenüber sequentieller Ausführung.

Ruby 4.0 überarbeitet das Ractor-API grundlegend. Die alten Methoden Ractor.yield, Ractor#take und die close_*-Methoden wurden entfernt. An ihre Stelle tritt Ractor::Port für die Kommunikation zwischen Ractors.

Die wichtigste Regel: Ein Port kann nur von dem Ractor empfangen werden, der ihn erstellt hat. Für bidirektionale Kommunikation benötigt daher jeder Ractor seinen eigenen Port (siehe Listing 3)


# Port des Haupt-Ractors für Antworten
main_port = Ractor::Port.new

worker = Ractor.new(main_port) do |reply_port|
  # Worker erstellt eigenen Port für eingehende Nachrichten
  worker_port = Ractor::Port.new
  reply_port.send(worker_port)  # teilt seinen Port mit

  num = worker_port.receive     # empfängt von eigenem Port
  reply_port.send(num * 2)      # sendet Ergebnis zurück
end

worker_port = main_port.receive  # erhält Worker-Port
worker_port.send(21)             # sendet Aufgabe
puts main_port.receive           # => 42


Die strikte Isolation von Ractors bedeutet, dass nicht jedes Objekt zwischen ihnen ausgetauscht werden kann. Ruby unterscheidet zwischen teilbaren (shareable) und nicht-teilbaren Objekten. Unveränderliche Objekte sind automatisch teilbar:


Ractor.shareable?(42)       #=> true
Ractor.shareable?(:symbol)  #=> true
Ractor.shareable?("text")   #=> false


Per Deep Freeze lassen sich aber Objekte explizit teilbar machen:


config = Ractor.make_shareable({ host: "localhost" })


Neu in Ruby 4.0 sind Shareable Procs und Ractor-lokaler Speicher. Damit lassen sich auch komplexere Szenarien umsetzen, bei denen Funktionen zwischen Ractors geteilt oder Daten innerhalb eines Ractors persistiert werden müssen.

Ruby war und ist eine dynamisch typisierte Sprache und prüft Variablentypen werden erst zur Laufzeit statt bei der Kompilierung. Doch die Arbeit am optionalen Typsystem zeigt, dass statische Analyse und dynamische Flexibilität koexistieren können. Ruby 4.0 markiert einen wichtigen Meilenstein auf diesem Weg.

RBS (Ruby Signature) ist das offizielle Format für Typdefinitionen. Anders als Annotationen im Quellcode werden RBS-Definitionen in separaten .rbs-Dateien gepflegt – ähnlich wie in TypeScript die .d.ts-Dateien. Dieser Ansatz hat einen entscheidenden Vorteil: Bestehender Ruby-Code muss nicht verändert werden, Teams können Typdefinitionen schrittweise einführen (siehe Listing 4).


# sig/user.rbs
class User
  attr_reader name: String    # Pflichtfeld: muss String sein
  attr_reader age: Integer?   # Optional: Integer oder nil

  # Rückgabe: void (kein Rückgabewert relevant)
  def initialize: (String, ?Integer) -> void

  # Prädikatmethode: gibt bool zurück
  def adult?: -> bool
end


Steep, der Referenz-Typchecker für RBS, findet Bugs, die sonst erst zur Laufzeit auffallen würden. Ein vollständiges Beispiel zeigen die Listings 5 und 6.

Diese Fehler würden ohne Typsystem erst zur Laufzeit auffallen. Mit RBS und Steep werden sie bereits beim Entwickeln oder spätestens in der CI-Pipeline erkannt. Das spart nicht nur Debugging-Zeit, sondern verhindert auch, dass solche Bugs überhaupt in Produktion gelangen.

KI-gestützte Coding-Assistenten wie GitHub Copilot, Cursor oder Claude generieren heute ganze Funktionen und Klassen auf Knopfdruck. Doch Large Language Models halluzinieren – sie erfinden Methodennamen, verwechseln Parameter-Reihenfolgen oder übergeben Strings, wo Integers erwartet werden. Bei dynamisch typisierten Sprachen wie Ruby fallen solche Fehler erst zur Laufzeit auf – im schlimmsten Fall in Produktion.

Hier entfaltet das RBS-Typsystem seinen vollen Wert: Steep fungiert beim Agentic Coding als Sicherheitsnetz. Generiert ein Assistent eine Funktion, die User.find_by_email mit einem Integer statt String aufruft, meldet Steep den Fehler sofort – noch bevor der Code ausgeführt wird. Die Feedback-Schleife verkürzt sich von „Laufzeitfehler nach Deployment“ auf „rote Unterstreichung im Editor“.

Noch wichtiger: RBS-Definitionen verbessern die Qualität der KI-Vorschläge selbst. Coding-Assistenten nutzen den Kontext – und Typsignaturen sind extrem dichter Kontext. Eine RBS-Datei dokumentiert nicht nur, welche Typen eine Methode akzeptiert, sondern kommuniziert auch die Intention des Codes. KI-Modelle, die auf Typdefinitionen trainiert wurden, generieren präziseren Code, weil sie die Constraints verstehen. Das Zusammenspiel in der Praxis:

  1. Entwickler schreibt RBS-Signatur für neue Methode
  2. KI-Assistent generiert Implementation basierend auf Signatur
  3. Steep validiert generierten Code gegen Typdefinition
  4. Fehler werden sofort sichtbar, Korrektur erfolgt vor Commit

Für Teams, die intensiv mit KI-Assistenten arbeiten, ist ein Typsystem oft keine optionale Ergänzung mehr – es ist die Qualitätssicherung, die verhindert, dass halluzinierter Code in die Codebasis gelangt. Ruby mit RBS bietet hier das Beste aus beiden Welten: die Flexibilität einer dynamischen Sprache mit der Sicherheit statischer Analyse, genau dort, wo man sie braucht.

Das langfristige Ziel ist ein Ökosystem der graduellen Typisierung. Entwickler sollen selbst entscheiden können, wie viel statische Analyse sie wünschen – von gar keine bis strikt überall. Anders als TypeScript, das JavaScript mit Typen erweitert, bleibt Ruby syntaktisch unverändert. Die Typen leben in separaten Dateien und sind vollständig optional.

Die Bausteine für dieses Ökosystem sind bereits vorhanden:

  • RBS Collection: Eine wachsende Bibliothek von Typdefinitionen für populäre Gems. Die IDE RubyMine lädt diese automatisch herunter und nutzt sie für Autovervollständigung und Fehlerprüfung. In VS Code ist die manuelle Einrichtung via rbs collection install nötig, danach funktioniert die Autovervollständigung mit der Ruby LSP Extension.
  • Steep: Der offizielle statische Typchecker, der RBS-Definitionen gegen den Quellcode prüft und in CI-Pipelines integriert werden kann.
  • TypeProf: Ein Inferenz-Tool, das aus bestehendem Code automatisch RBS-Definitionen generiert – ideal für die schrittweise Einführung von Typen in Legacy-Projekten.
  • Sorbet-Integration: Stripes alternativer Type-Checker erhöht die RBS-Kompatibilität, was die Interoperabilität zwischen beiden Systemen verbessert.

Ein Parser ist das Programm, das Quellcode liest und in eine strukturierte Darstellung übersetzt – den Abstract Syntax Tree (AST). Erst durch diese Baumstruktur kann der Interpreter verstehen, was der Code bedeutet. Seit Ruby 3.4 ist Prism der Standard-Parser und ersetzt den 30 Jahre alten parse.y. Prism wurde in C99 ohne externe Abhängigkeiten geschrieben, ist fehlertolerant und portabel.

Die Benchmarks sprechen für sich: Prism ist 2,56-mal schneller beim Parsen zu C-Structs gegenüber parse.y und zwölfmal schneller als das Parser-Gem beim AST-Walk. Für Entwickler bedeutet das schnellere IDE-Reaktionen und kürzere CI-Zeiten. Bei Kompatibilitätsproblemen kann der klassische Parser weiterhin aktiviert werden:


ruby --parser=parse.y script.rb


Für die meisten Projekte sollte Prism jedoch problemlos funktionieren.

Wer Ruby 4.0 parallel zu älteren Versionen betreiben möchte, braucht einen Version Manager. Diese Tools lösen ein grundlegendes Problem: Jedes Ruby-Projekt kann eine andere Ruby-Version erfordern, und Gems sind nicht zwischen Ruby-Versionen kompatibel.

Version Manager installieren mehrere Ruby-Versionen isoliert voneinander – typischerweise unter ~/.rvm, ~/.asdf oder ~/.local/share/mise. Jede Ruby-Version erhält ihr eigenes Verzeichnis mit einem eigenen gem-Ordner. Fürht man also gem install rails unter Ruby 3.3 aus, landet Rails in einem anderen Verzeichnis als unter Ruby 4.0. Gems müssen daher für jede Ruby-Version separat installiert werden. Bundler (bundle install) erledigt das automatisch basierend auf dem Gemfile.

Welche Ruby-Version für ein Projekt gilt, bestimmt eine Datei im Projektverzeichnis: .ruby-version (einfacher Standard) oder .tool-versions (für asdf und mise, kann auch Node, Python etc. definieren). Wechselt man ins Projektverzeichnis, aktiviert der Version Manager automatisch die richtige Ruby-Version.

Der erste populäre Ruby Version Manager war RVM. Er modifiziert die Shell-Umgebung tiefgreifend und verwaltet zusätzlich Gemsets – isolierte Gem-Umgebungen pro Projekt. Das war vor Bundler (2010) revolutionär, da es keine andere Möglichkeit gab, Gem-Abhängigkeiten pro Projekt zu isolieren. Heute sind Gemsets obsolet, da Bundler diese Aufgabe besser löst.

asdf löste RVM für viele Teams ab. Der entscheidende Vorteil: Ein Tool für alle Sprachen. Über Plugins verwaltet asdf Ruby, Node.js, Python, Elixir und dutzende weitere Runtimes einheitlich. Die .tool-versions-Datei im Projektverzeichnis definiert alle benötigten Versionen. asdf ist weniger invasiv als RVM, in Bash geschrieben und integriert sich sauber in die Shell.

Der aktuelle Trend geht zu mise, benannt nach dem Mise en place bei Köchen. Entwickelt vom asdf-Maintainer Jeff Dickey, ist mise ein kompletter Rewrite in Rust. Die Vorteile: deutlich schneller (Rust statt Bash), kompatibel mit asdf-Plugins und .tool-versions-Dateien, aber auch mit eigenen Backends. mise aktiviert Versionen ohne Shell-Hooks über Shims – ein einfaches mise activate in der Shell-Konfiguration genügt. Zudem kann mise Umgebungsvariablen und Tasks verwalten, was es zu einem universellen Manager für Entwicklungsumgebungen macht. So wird Ruby 4 mit mise installiert:


mise install ruby@4.0.0
mise use ruby@4.0.0
mise activate  # einmalig in .bashrc/.zshrc


Für neue Projekte ist mise die beste Wahl. Es ist schnell, modern und vielseitig. Bestehende asdf-Setups funktionieren weiter, mise liest deren Konfiguration. RVM-Nutzer sollten den Umstieg erwägen.

Die praktische Breaking-Change-Bilanz von Ruby 4.0 ist moderat. Fedora bewertet: Da sich mit Ruby 4.0 der soname, also der Bezeichner für Shared Libraries ändert, müssen Pakete mit binären Erweiterungen neu gebaut werden. Da aber große Aufmerksamkeit auf Quellkompatibilität gelegt wurde, sind keine Code-Änderungen nötig. Weitere Breaking Changes sind:

  • Binding#local_variables enthält keine nummerierten Parameter mehr
  • ObjectSpace._id2ref ist deprecated
  • CGI-Library aus Default Gems entfernt (nur cgi/escape bleibt)
  • SortedSet entfernt und erfordert die Gem sorted_set
  • String-Literal-Warnung: In Dateien ohne frozen_string_literal-Kommentar erzeugt Mutation eine Deprecation-Warnung

Problematisch bleibt die Pessimistic-Constraint-Praxis vieler Gems: ~> 3.x in required_ruby_version verhindert die Installation unter Ruby 4.0, auch wenn der Code ohne Änderungen laufen würde.

Für Entwickler bedeutet Ruby 4.0 vor allem Kontinuität: Bestehender Code läuft weiter, die Performance verbessert sich weiter (YJIT bietet 92 Prozent Speedup gegenüber dem Interpreter), und das Typsystem reift. Die eigentliche Innovation liegt in der Infrastruktur für die nächste Dekade – ZJIT, Modular GC und die verbesserten Ractors werden Ruby für die kommenden Jahre wettbewerbsfähig halten.


(ulw)



Source link

Weiterlesen

Entwicklung & Code

Software Testing: Weihnachtsplausch zu Software-Tests


In dieser Episode sprechen Richard Seidl, Christian Mercier, Matthias Gross und Wolfgang Sperling über das Testerjahr 2025, KI im Alltag und Erwartungen an 2026. Nichtfunktionale Qualität rückt nach vorn: Security, Performance, Usability, Compliance. Gefragt sind T-Shaped Skills, technisches Verständnis und Haltung: Intuition, Mut, Resilienz.

Weiterlesen nach der Anzeige

Themen im Gespräch sind auch souveräne Cloud, Druck aufs Agile und mehr Validierung in der Produktion. Die Runde betont den Wert aktiver Communitys wie Testland und fragt: Wie gelingt gemeinsames Lernen, das konkrete Probleme löst?

Matthias Groß ist Partner von TestGilde und seit 2007 als Berater für Softwarequalitätssicherung und Testmanagement tätig. Seine Schwerpunkte liegen im operativen Testmanagement, der Einführung und Weiterentwicklung von Testmanagementstrukturen sowie der Betreuung kundenspezifischer Testservices. Er engagiert sich zudem an der Dualen Hochschule Baden-Württemberg, ist Mitgründer der Testcommunity The TestLänd und Mitglied des Programmkomitees des QS-Tags.

Christian Mercier begleitet IT-Projekte im Banking-Umfeld. In den klassischen, agilen oder hybriden Projekten nimmt er verschiedene Rollen ein – er ist Projektleiter, Coach, Testmanager, Business-Analyst oder Requirement-Engineer – aber das Thema Qualität steht für ihn immer an zentraler Stelle. Ihm geht es immer um pragmatische Lösungen, die auf fundierten Entscheidungen im jeweiligen Kontext beruhen.

Wolfgang Sperling ist Solution Architekt bei Avanade und Ansprechpartner für den Technologiestack von Microsoft im Kompetenzzentrum für digitale Souveränität bei Accenture. Seit mehr als 15 Jahren beschäftigt er sich mit Qualitätssicherung in Softwareprojekten, einen großen Anteil davon in kritischen oder herausfordernden Projektsituationen. In der Qualitätssicherung ist es ihm wichtig, neben Testen und Testautomatisierung auch Anforderungsmanagement und Releasemanagement in den Softwarelebenszyklus einzuschließen.

Bei diesem Podcast dreht sich alles um Softwarequalität: Ob Testautomatisierung, Qualität in agilen Projekten, Testdaten oder Testteams – Richard Seidl und seine Gäste schauen sich Dinge an, die mehr Qualität in die Softwareentwicklung bringen.

Weiterlesen nach der Anzeige

Die aktuelle Ausgabe ist auch auf Richard Seidls Blog verfügbar: „Software-Test Weihnachtsplausch – Christian Mercier, Matthias Gross und Wolfgang Sperling“ und steht auf YouTube bereit.


(mdo)



Source link

Weiterlesen

Beliebt