Ruby on Rails wird zehn Jahre alt

Zum Geburtstag: Queueing, Performance und Fremdschlüssel

Nachdem Rails vor einigen Jahren durch progressive Ansätze fast schon eine Revolution in der Welt der Webentwicklung auslöste und damit unzählige Nachahmer fand, wurde es in den letzten Jahren etwas ruhiger um das Framework. Es hat sich mittlerweile als Standard etabliert, an dem Alternativen und Nachahmer - auch aus anderen Sprachen - gemessen werden. Dieser Artikel blickt auf die Meilensteine und Verdienste des Frameworks zurück und wagt einen Ausblick, wie die Fahrt auf Schienen weitergeht.

Wie alles anfing

Die dynamische Programmiersprache Ruby war unter westlichen Entwicklern bis 2004 noch weitgehend unbekannt. Erste Popularität erlangte sie zwar durch das Buch der Pragmatic Programmers (wegen des Titelbilds unter dem Namen „Pickaxe“ bekannt), richtig bekannt wurde die Sprache jedoch erst, als David Heinemeier Hansson (auch bekannt als DHH) 2004 die erste Version von Ruby on Rails quelloffen bereitstellte und damit einiges an Aufsehen erregte.

Das Framework entsprang aus der Entwicklung der Projektmanagement-Software Basecamp von 37signals (heute ebenfalls: Basecamp), der Firma, in der DHH bis heute als Partner agiert. Dadurch dass Rails aus der Entwicklung einer realen Webanwendung extrahiert wurde, war es von Beginn an durch einen starken Pragmatismus geprägt. Anwender des Frameworks sollten sich nicht weiter damit beschäftigen müssen, wo sie welchen Quellcode ablegen und wie sie ihre Konfiguration verwalten. Gepaart mit der Syntax und den Sprachmitteln von Ruby sollte das für eine bis zu diesem Zeitpunkt unbekannte Produktivität in der Entwicklung von Webanwendungen sorgen.

Letzten Endes sorgte DHH dann mit dem berühmten Screencast „Creating a Weblog in 15 Minutes“ für einen Paukenschlag unter Webentwicklern: In dem Video führte er eindrucksvoll vor, wie schnell sich mit Ruby on Rails lauffähige Webanwendungen erstellen lassen. Wichtiger jedoch als die Möglichkeit, mit einer Demo zu beeindrucken, war und sind jedoch immer die grundlegenden Entwurfsprinzipien, denen das Framework folgt.

„It’s all about managing complexity“

Steve Jobs hatte 1997 in seiner Abschlussrede zur WWDC vom Komplexitätsmanagement in der Softwareentwicklung gesprochen. Dieses spielt auch heute noch eine wichtige Rolle. Wartbarer Programmcode sollte cleverem Code stets vorgezogen werden - Rails begegnet diesem Credo mit den Prinzipien „Don’t repeat yourself“ (DRY) und „Convention over Configuration“. Die beiden Konzepte stehen im Mittelpunkt der gesamten Entwicklung und haben das Framework in seiner Evolution geprägt.

Das DRY-Prinzip wurde zum ersten Mal 1999 in dem Buch „The Pragmatic Programmer“ erwähnt. Es besagt, dass innerhalb eines Systems jeglicher Zustand nur eine eindeutige, verbindliche Darstellung besitzt. Durch Befolgen dieses Prinzips lassen sich Redundanzen und Code-Duplizierungen vermeiden. Bei Rails können deswegen Entwickler Änderungen isoliert und einmalig an zentraler Stelle vornehmen. Zum Beispiel gibt es keine Duplikation zwischen den Spalten einer Datenbanktabelle und den Attributen von Ruby-Objekten, die für das Verarbeitung von Tabellenzeilen zum Einsatz kommen.

Das Prinzip „Convention over Configuration“ ist damit eng verzahnt. Es definiert Regeln und Konventionen, durch die sich Entwickler bei der täglichen Arbeit auf die wesentlichen Aufgaben fokussieren können sollen, ohne jedoch die Freiheit einzuschränken. Halten sie sich an diese Regeln, entfällt oder vereinfacht sich die Konfiguration und erlaubt ihnen die zügige Umsetzung von Anforderungen. DHH begründet das Prinzip mit der Aussage, dass Menschen für sie getroffene Entscheidungen bevorzugen, statt sie selbst immer wieder neu zu fällen.

Dieser Sachverhalt lässt sich wieder gut mit dem objektrelationalen Mapping von ActiveRecord erläutern. Modellklassen werden in Rails per Konvention immer im Singular bezeichnet (z.B. „customer“), Instanzen der jeweiligen Klasse hingegen auf eine Datenbanktabelle mit einem Namen im Plural abgebildet („customers“). Ein weiteres Zusammenspiel stellt die Verwendung von Datenbankrelationen dar: Das Framework erwartet hier standardmäßig eine Fremdschlüsselspalte mit dem Namen „customer_id“ und versucht, die Relation über die Datenbanktabelle „customers“ mit der Spalte „id“ aufzulösen.

Für das beschriebene Verhalten ist keine explizite Konfiguration notwendig. Sollte das Standardverhalten nicht ausreichen oder nicht den Anforderungen entsprechen, lässt es sich einfach anpassen und zentral überschreiben.

Shared Tool, Shared Solution

Früher lebten viele Entwickler in dem Glauben, sie bräuchten für ihre Anwendungsfälle Spezialwerkzeuge, um ihre spezifischen Anforderungen umzusetzen. Das Rails Framework hingegen ist kein Spezialwerkzeug, sondern eher ein Standardbaukasten für die Webentwicklung. Erprobte Techniken sollen die Antwort auf sich oft ähnelnde Fragen und Anforderungen bieten.

So wächst das Framework seit dem ersten Release um Techniken für wiederkehrende Problemstellungen. Das Core-Team ist der Meinung, dass Webanwendungen meist Daten persistieren. Daher ist die Persistenzschicht in Form von ActiveRecord Teil des Frameworks. Seit Version 3.1 bietet die sogenannte Asset-Pipeline eine Methode zum Preprocessing sowie zur Zusammenführung und Minifizierung von JavaScript- und Stylesheet-Dateien - eben weil eine solche Funktion häufig benötigt wird. Das Vorgehen spiegelt sich auch in der anstehenden Veröffentlichung der Version 4.2 durch die Einführung einer generischen API für Hintergrundprozesse wider.

Ein Rückblick auf die Meilensteine

15 Monate nach der ersten öffentlichen Vorstellung durch Heinemeier Hansson erschien am 13. Dezember 2005 die Version 1.0 des Frameworks. Das erste Major-Release des Frameworks hatte bereits einen beachtlichen Funktionsumfang und bestand aus folgenden Modulen:

  • ActiveRecord zur Datenbankabstraktion,
  • ActionPack zur Kapselung des Controller- und View-Layers
  • ActionMailer für den Versand von E-Mails,
  • ActionWebService zum Bereitstellen und Konsumieren von Webservices sowie
  • ActiveSupport mit Spracherweiterungen von Ruby und unterstützenden Funktionen für das Framework.

Zu den Kern-Features zählten das HTTP-Routing, der noch heute enthaltene HTTP-Server WEBrick, Caching, Validatoren, Assoziationen, Datenbankmigrationen und diverse Hilfsmethoden zur Implementierung von AJAX-Funktionen über Prototype bzw. Scriptaculous. ActiveRecord lieferte Adapter für die Datenbanken MySQL, PostgreSQL, SQL Server, SQLite, DB2 und Oracle.

In weiteren Minor-Releases wurden bis Januar 2007 die Performance optimiert und neue Features hinzugefügt, unter anderem Eager Loading, Einbindung von Integrationstests sowie verbesserte Unterstützung von HTTP inklusive Status-Codes, MIME-Types und verschiedener Ressourcenrepräsentationen.

Die Liebe zu HTTP

Der vom Core-Team eingeschlagene Weg setzte sich in weiteren Versionen fort: Rails 2.0 erschien im Dezember 2007 und stellte REST in den Mittelpunkt - das Team nahm damit eine klare Position in der Debatte SOAP versus REST ein und hat damit nicht unerheblich zur Popularität von REST beigetragen. Unter anderem musste das Modul ActionWebService zugunsten von ActiveResource weichen und Entwicklern wurden weitere Werkzeuge und Konventionen mit an die Hand gegeben, welche die Entwicklung von RESTful-Applikationen erleichterten.

ActiveResource bildet Modellklassen auf REST-konforme Ressourcen ab und war über Jahre Kernbestandteil des Frameworks. ActionWebService stand weiterhin als Ruby-Gem, also als externe Bibliothek, zur Verfügung.

Der Lebenszyklus der 2.x-Version brachte weitere Features hervor: das Internationalisierungsmodul (i18n), Engines, Thread-Sicherheit und Kompatibilität zur alternativen, in Java geschriebenen Ruby-Implementierung JRuby. Die Entwicklung des Frameworks findet seither über die Kollaborationsplattform GitHub statt.

Rails und Merb werden eins

Bei der Entwicklung von Rails 3.0 lag das Augenmerk auf Modularisierung. Rails wurde in diesem Zuge mit dem ebenfalls in Ruby implementierten Webframework Merb zusammengeführt. Die Merb-Entwickler, allen voran Yehuda Katz, verwirklichten dessen modularen Ansatz auch in Rails. Beispielsweise wurden Validatoren und Callbacks von ActiveRecord in das neue Modul ActiveModel extrahiert.

Heinemeier Hansson verglich 2012 auf seinem Blog Rails mit dem japanischen Begriff „Omakase“: ein Menü, bei dem man dem Koch das Vertrauen ausspricht und ihm die Zusammenstellung der Speisen überlässt. Der Leitsatz „Rails is Omakase“ gilt zwar bis heute, die Modularisierung ermöglichte jedoch von nun an den Austausch von Standardkomponenten des Frameworks. So stand der Nutzung alternativer Test-Frameworks wie RSpec oder Persistenzmappern wie Sequel nichts mehr im Wege. Eine weitere große Neuerung war die Einführung von Bundler als Antwort auf die bis dato ungenügende Abhängigkeitsverwaltung in Rails.

In den auf Rails 3.0 folgenden Releases ging man wieder zur iterativen Einführung neuer Features über. So wurde die Asset-Pipeline eingeführt, jQuery löste das JavaScript-Framework Prototype ab, und SASS und CoffeeScript wurden standardmäßig als CSS- und JavaScript-Abstraktionen integriert.

Die kleine Vier

Rails 4.0 wurde am 25. Juni 2013 veröffentlicht. Im Gegensatz zur kompletten Kernsanierung in Version 3.0 erfuhr Rails nun mehrheitlich iterative Verbesserungen. Die großen Umbauarbeiten durch Yehuda Katz und das Team tragen bis heute ihre Früchte: Ein Rütteln an der grundlegenden Framework-Architektur war seither nicht mehr nötig.

Zu den Neuerungen in Version 4 zählten Turbolinks zum Optimieren der clientseitigen Performance, Russian-Doll-Caching und Live-Streaming. Die Version 4.1 brachte dann den Preloader Spring, Enums für ActiveRecord sowie ein generisches Interface zur Verwendung von Umgebungsvariablen.

Neuheiten in Rails 4.2

ActiveJob

An vorderster Front des neuen Releases steht sicherlich ActiveJob, eine einheitliche API für Queue-Systeme wie Resque und Delayed Job. In der Webentwicklung gibt es seit jeher den Bedarf, bestimmte Aufgaben außerhalb des Request/Response-Zyklus ausführen zu lassen. Ein prominentes Beispiel ist das Versenden von E-Mails: Blockiert der Prozess, beispielsweise durch einen langsam reagierenden Mail-Server, leidet darunter auch direkt die Antwortzeit der eigentlichen Anwendung.

Dieser häufig begangene Fauxpas ist dem Core-Team bekannt, und genau dort setzt es an: Neben der eigentlichen Framework-Komponente ActiveJob stellt es eine native Integration in ActionMailer bereit. Ab sofort sollten E-Mails, die mit ActionMailer versendet werden, explizit mit deliver_now oder deliver_later übermittelt werden. Wichtig zu beachten ist dabei, dass Rails ohne weiteres Zutun der Entwickler standardmäßig eine synchrone Ausführung über ActiveJob durchführt. Man muss sich also weiterhin eigenständig um eine Lösung zur asynchronen Ausführung von Aufgaben kümmern, ActiveJob agiert dabei „nur“ standardisierend als einheitliche API für Bibliotheken wie Resque und Delayed Job.

Verbesserungen bei der Performance

Aaron Patterson, Mitglied des Core-Teams, verpasste seinen Performance-Optimierungen von ActiveRecord den nicht ganz erst gemeinten Titel „Adequate Record“. Wie sich herausstellte, hatten einige Finder-Methoden in ActiveRecord deutliches Verbesserungspotenzial in Sachen Performance. Laut Patterson werden einige Aufrufe nun bis zu zweimal schneller ausgeführt - etwa einfache Anfragen wie

User.find(1)
User.find_by_name('Arnulf Beckenbauer')

oder auch das Laden von Relationen durch Assoziations-Helper wie

user.orders

Erreicht wird das durch das Caching von generierten Query-Fragmenten. Bisher ließ sich das für einfache Primärschlüsselselektionen und Finder-Methoden (s.o.) bewerkstelligen, da in diesen Fällen keine Query-Generierung durch Methodenverkettung ermöglicht wird - Rails muss keine Relationsobjekte (ActiveRelation) erstellen.

Hilfreiche Web Console

Mit der Web Console wurde ein hilfreiches Tool eingeführt, mit dem das lokale Debugging deutlich einfacher von der Hand geht. Die bisherigen Fehlerseiten, die den Stacktrace einer Exception im Browser darstellen, bieten nun auch im unteren Bereich eine Terminal-artige Konsole an, wo sich beliebiger Ruby-Code eingeben und ausführen lässt. Die Ausführung erfolgt dabei im Kontext der aktuellen Exception. Das heißt, der Code wird an der Stelle ausgeführt, an welcher der Fehler aufgetreten ist.

Darüber hinaus kann man die Web Console auch in einem selbst definierten Kontext des aktuellen Requests ausführen: Ruft man den Helper console im aktuellen Controller oder View auf, legt sich die Konsole im Browser über die aktuelle Seite. Das ermöglicht schnelles interaktives Debugging im Geiste von Drittbibliotheken wie pry.

Wer die Web Console in bestehenden Rails-Anwendungen nach dem Update auf 4.2 nutzen will, muss noch einen entsprechenden Eintrag im Gemfile vornehmen:

group :development do
  gem 'web-console', '~> 2.0'
end

Fremdschlüssel in der Datenbank

Viele langjährige Rails-Entwickler werden die Einführung von echten Foreign-Key-Constraints sicherlich mit offenen Armen begrüßen. Lange Jahre wurde die Datenbank durch Rails nur stiefmütterlich als relativ „dumme“ Persistenzschicht behandelt. Nun scheint man auch im Core-Team erkannt zu haben, dass es nicht unbedingt zweckdienlich ist, viele Errungenschaften der relationalen Datenbanken einfach zu ignorieren. Echte Foreign-Key-Constraints auf Datenbank-Ebene bieten zahlreiche Vorteile: Modifizieren Entwickler Tabelleninhalte händisch mit SQL und nicht über die Rails-Anwendung, kann es schnell passieren, dass sie Inkonsistenzen erzeugen. Löschen sie etwa händisch eine Zeile aus der primären Tabelle (1) einer 1:n Beziehung, so fehlt den Zeilen in der sekundären Tabelle (n) nun die Beziehung, wodurch sie gegebenenfalls inkonsistent werden. Ein Foreign-Key Constraint hätte das Löschen beispielsweise unterbunden oder die betroffenen Relationen gleich mitgelöscht (Cascading Delete). Der Ansatz von Rails war seit jeher, solcherlei Logik in den Modellklassen zu definieren:

has_many :comments, dependent: :destroy

Vergisst man die dependent-Option, entstehen schnell Inkonsistenzen. Auch bei korrekter Definition beschränkt sich das Verhalten lediglich auf CRUD-Aktionen, die die Rails-Anwendung auslöst. Zugriffe, die beispielsweise direkt über SQL-Werkzeuge erfolgen, sind damit außen vor. Foreign-Key Constraints lassen sich über Datenbankmigrationen, das heißt über den Ruby-Code, der für das Erzeugen der Datenbank-DDL-Statements zuständig ist, hinzufügen beziehungsweise entfernen, sodass das DRY-Prinzip erhalten und Duplikation vermieden wird. Zum Release-Zeitpunkt von Rails 4.2 wird das von den Datenbankadaptern für MySQL und PostgreSQL unterstützt.

Kleinvieh macht auch Mist

Neben den großen neuen Features gibt es wie in jedem größeren Release noch unzählige kleine Erweiterungen und Verbesserungen. Eine davon ist ein komplett neuer HTML-Sanitizer, der auf Nokogiri basiert. Man verspricht sich davon eine robustere Implementierung: Nokogiri ist der am meisten genutzte HTML- und XML-Parser im Ruby-Ökosystem.

Ebenfalls neu ist ein für alle Konfigurationen angeglichener Standardwert für den Log-Level. Rails verwendet nun in der „production“-Umgebung ebenfalls den Level :debug zum Loggen.

Außerdem gibt es nun einen extra Namespace (x) in der Rails-Konfiguration, worin Entwickler anwendungsspezifische Konfiguration ablegen können, die nicht direkt etwas mit dem Framework zu tun hat:

# config/environments/production.rb
config.x.meine_app.newsletter_sender = 'newsletter@meine-app.com'

Darüber hinaus wurde wieder Entrümpelung betrieben: respond_with und respond_to auf Klassenebene wurden in ein separates Gem „responders“ verschoben.

Wohin geht die Fahrt?

Die Grundsätze und Philosophien von Rails sind bis heute unverändert und haben erheblich zum Umdenken bei vielen anderen Webframeworks geführt. Der bahnbrechende Erfolg sorgte dafür, dass über die Jahre auch etliche Klone in anderen Programmiersprachen entstanden, unter anderem Frameworks wie Grails (vormals Groovy on Rails) und Play!.

Rails 4.2 ist das letzte große Release der 4er-Reihe. Seit Veröffentlichung des ersten Release Candidate zeigt der Hauptentwicklungszweig auf die Version 5.0.0.alpha. Die nächste Generation soll Vorzüge von Ruby 2.2 nutzen. Hierzu zählt vor allem, dass Ruby-Symbole zukünftig der Garbage Collection (GC) unterliegen und bei der automatischen Speicherbereinigung berücksichtigt werden.Darüber hinaus werden Metaprogrammierungs-Patches wie alias_method_chain entfallen können, da Ruby nun entsprechende Sprachmittel zur Verfügung stellt.

Die aktuelle Roadmap strebt eine Veröffentlichung im Frühjahr oder Sommer 2015 an. Rails steht zwar schon lange nicht mehr allein auf weiter Flur, zeigt aber sowohl mit dem aktuellen wie auch den angekündigten Releases keinerlei Ermüdungserscheinungen.

TAGS

Kommentare

Um die Kommentare zu sehen, bitte unserer Cookie Vereinbarung zustimmen. Mehr lesen