Recordables in Rails: Delegated Types praxisnah erklärt

In diesem Beitrag zeige ich dir das Recordable Pattern, das 37signals (Basecamp, HEY) einsetzt, und erkläre es Schritt für Schritt anhand von lauffähigen Rails-Code-Beispielen. Zielgruppe sind Anfänger bis Fortgeschrittene Ruby on Rails Entwickler, die polymorphe Datenmodelle sauber, skalierbar und gut paginierbar bauen möchten.

Warum das Ganze? Klassische Ansätze wie Single Table Inheritance (STI) oder „nackte“ polymorphe Assoziationen stoßen schnell an Grenzen: STI bläht Tabellen auf, polymorphes CRUD bleibt oft mühsam zu paginieren. Das Recordable Pattern nutzt Rails’ Delegated Types, um eine dünne, performante „Superklasse“-Tabelle (Recording) mit schlanken „Subklasse“-Tabellen (Recordables wie Message, Document, Comment) zu kombinieren.


Das Grundprinzip

  • Eine zentrale Tabelle „recordings“ hält alle gemeinsamen Metadaten (z. B. Bucket, Creator, Timestamps) und verweist polymorph auf den „konkreten Inhalt“ (recordable).
  • Jede konkrete Recordable (Message, Document, Comment) hat eine eigene Tabelle mit genau ihren Feldern.
  • Abfragen, Pagination und gemeinsame Logik passieren über Recording; konkretes Verhalten (z. B. export_html) lebt bei den Recordables.

So bleibst du:

  • flexibel (neue Typen ohne Recording-Migration),
  • performant (einheitliche Timeline auf der dünnen Recording-Tabelle),
  • sauber getrennt (Metadaten vs. Inhalte).

Minimales Datenmodell

Migration (gekürzt auf das Wesentliche):

create_table :recordings do |t|
  t.references :bucket,  null: false, foreign_key: true
  t.references :creator, null: false, foreign_key: { to_table: :users }
  t.string  :recordable_type, null: false
  t.bigint  :recordable_id,   null: false
  t.index [:recordable_type, :recordable_id]
  t.bigint :parent_id # optional: Recording-Baum (Unterelemente)
  t.index :parent_id
  t.timestamps
end
add_foreign_key :recordings, :recordings, column: :parent_id

create_table :messages do |t|
  t.string :title,   null: false
  t.text   :content, null: false
  t.timestamps
end

create_table :documents do |t|
  t.string :title, null: false
  t.text   :body
  t.string :external_url
  t.timestamps
end

create_table :comments do |t|
  t.text :body, null: false
  t.timestamps
end

Models und Concerns

Recording: die „Superklasse“ mit Delegation

class Recording < ApplicationRecord
  belongs_to :bucket
  belongs_to :creator, class_name: "User"

  delegated_type :recordable, types: %w[Message Document Comment], dependent: :destroy

  # optional: Baumstruktur
  belongs_to :parent, class_name: "Recording", optional: true
  has_many   :children, class_name: "Recording", foreign_key: :parent_id, dependent: :nullify

  # Bequeme Filter-Scopes kommen mit delegated_type:
  # Recording.messages, Recording.documents, Recording.comments
end

Recordable-Concern: gemeinsame API für Inhalte

module Recordable
  extend ActiveSupport::Concern

  included do
    has_many :recordings, as: :recordable, inverse_of: :recordable
  end

  def display_title
    respond_to?(:title) ? title : self.class.name
  end

  def export_html
    "#{display_title}"
  end
end

Konkrete Typen:

class Message < ApplicationRecord
  include Recordable
  validates :title, :content, presence: true

  def export_html
    "#{title}#{content}"
  end
end

class Document < ApplicationRecord
  include Recordable
  validates :title, presence: true

  def export_html
    if external_url.present?
      "#{title}External: #{external_url}"
    else
      "#{title}#{body}"
    end
  end
end

class Comment < ApplicationRecord
  include Recordable
  validates :body, presence: true

  def display_title
    body.truncate(40)
  end

  def export_html
    "#{body}"
  end
end

Erstellen und Paginieren einer Timeline

Bucket kapselt die Aufnahme neuer Inhalte („record“) und die Timeline. Bucket ist hier eher abstrakt und könnte genausogut auch ein Projekt sein, oder ein Kunde:

class Bucket < ApplicationRecord
  has_many :recordings, dependent: :destroy

  def timeline(limit: 50)
    recordings.order(created_at: :desc).limit(limit).includes(:recordable)
  end

  def record(recordable, creator:, parent: nil, color: nil)
    recordings.create!(recordable: recordable, creator: creator, parent: parent, color: color)
  end
end

Typische Abfragen:

bucket = Bucket.find(1)

# Gesamte Timeline effizient paginieren
bucket.timeline(limit: 50).each do |rec|
  puts "[#{rec.recordable_type}] #{rec.recordable.display_title}"
end

# Nur bestimmte Typen (Scopes via delegated_type)
Recording.messages.where(bucket: bucket).limit(20)
Recording.documents.where(bucket: bucket).order(created_at: :desc)

Versionierung per Repointing (immutable Recordables)

Statt Inhalte zu „updaten“, erzeugst du neue Recordables und verweist die Recording-Zeile auf die neue Version. Das hält Historie sauber und macht Kopieren effizient.

class Recording < ApplicationRecord
  # ...

  def repoint_to!(new_recordable)
    update!(recordable: new_recordable)
  end
end

# Beispiel: Message ? neue Document-Version
msg_rec = bucket.record(Message.create!(title: "Kickoff", content: "Welcome!"), creator: user)
new_doc = Document.create!(title: "Specs v2", body: "Updated requirements")
msg_rec.repoint_to!(new_doc) # Recording zeigt nun auf Document

Wenn du eine echte Ereignis-Historie brauchst, ergänze ein Event-Model und schreibe beim record/repoint_to! Einträge (z. B. „created“, „repointed“, inkl. vorher/nachher recordable_type/_id). Das hält Audit-Trails und erlaubt Timeline-Features (siehe Video/37signals-Artikel).


Copy & Move: Subtrees kopieren

Ein Vorteil des Patterns: Kopieren und Verschieben ganzer Teilbäume wird planbar und schnell, weil Recordings leichtgewichtig sind und Recordables immutable dupliziert werden. Eine einfache Kopier-Serviceklasse:

class Copier
  def self.copy!(source_recording:, destination_bucket:, creator:)
    new(source_recording, destination_bucket, creator).copy!
  end

  def initialize(source_recording, destination_bucket, creator)
    @source_recording   = source_recording
    @destination_bucket = destination_bucket
    @creator            = creator
  end

  def copy!
    ActiveRecord::Base.transaction do
      copied_root = copy_recording(@source_recording, parent: nil)
      @source_recording.children.each { |child| copy_branch(child, parent: copied_root) }
      copied_root
    end
  end

  private

  def copy_branch(node, parent:)
    copied = copy_recording(node, parent: parent)
    node.children.each { |child| copy_branch(child, parent: copied) }
    copied
  end

  def copy_recording(original, parent:)
    new_recordable = duplicate_recordable(original.recordable)
    @destination_bucket.record(new_recordable, creator: @creator, parent: parent, color: original.color)
  end

  def duplicate_recordable(recordable)
    case recordable
    when Message
      Message.create!(title: recordable.title, content: recordable.content)
    when Document
      Document.create!(title: recordable.title, body: recordable.body, external_url: recordable.external_url)
    when Comment
      Comment.create!(body: recordable.body)
    else
      raise ArgumentError, "Unsupported recordable: #{recordable.class.name}"
    end
  end
end

Vergleich mit STI und „plain“ Polymorphismus

  • STI: Einfache Fälle ok. Bei divergenten Typen entsteht Tabellen-Bloat, viele NULL-Spalten, Migrationen werden groß, Erweiterbarkeit leidet.
  • Polymorphismus ohne delegated_type: Funktioniert, aber es fehlen bequeme Scopes/Convenience-Methoden; unified Pagination ist oft hakelig.
  • Recordables + delegated_type: Einheitliche Timeline über eine dünne Tabelle, klare Trennung von Meta vs. Inhalt, leichte Erweiterbarkeit (neuen Typ hinzufügen statt zentrale Tabelle migrieren), gute Performance bei Abfragen.

Wann solltest du das Pattern nutzen?

  • Du brauchst eine gemischte, paginierbare Timeline (z. B. Aktivitätsfeed über Messages, Documents, Comments).
  • Deine Typen unterscheiden sich stark in ihren Attributen.
  • Du willst Polymorphismus am Parent (Recording) und saubere Delegation an die Inhalte.

Wann eher nicht?

  • Deine Subtypen sind fast identisch (STI könnte reichen).
  • Du hast keine gemeinsamen Abfragen über Typgrenzen hinweg.

Best Practices

  • Halte Recording schlank: nur Metadaten und die polymorphe Referenz. Keine großen Textfelder.
  • Packe typenübergreifende Logik in Recording (oder Concerns), typenspezifische Logik in die Recordables.
  • Nutze delegate am Recording für gemeinsame Schnittstellen, z. B. delegate :export_html, to: :recordable.
  • Wenn Auditing/History wichtig ist, ergänze Events und schreibe Änderungen mit.
  • Autorisierung: Recording bündelt viele Aktionen – prüfe Berechtigungen konsequent.

Fazit

Das Recordable Pattern mit delegated_type ist eine elegante, praxiserprobte Lösung für polymorphe Inhalte in Rails. Es bringt dir:

  • eine einheitliche, performante Timeline,
  • klare Verantwortlichkeiten zwischen Metadaten und Inhaltsobjekten,
  • einfache Erweiterbarkeit ohne große Migrationen,
  • saubere APIs durch Delegation.

Wenn du Feed-ähnliche Strukturen, Versionierung und Kopier-Features brauchst, wirst du mit Recordings/Recordables sehr schnell produktiv.

Viel Spaß beim Ausprobieren – und schreib mir gern, wenn du Fragen hast oder Beispiele aus deinem Projekt teilen willst!

— Mathias (webmatze.de)

Framework Vergleich: React vs. Svelte vs. Astro

React, Svelte oder Astro? Ein Leitfaden für Einsteiger

Wer heute mit der Webentwicklung beginnt, steht vor einer schier endlosen Auswahl an JavaScript-Frameworks. React dominiert mit einem Marktanteil von über 80%, aber immer mehr Entwickler entdecken Alternativen wie Svelte und Astro. Welches Framework ist das richtige für dich als Einsteiger?

In diesem Artikel vergleiche ich die drei wichtigsten JavaScript-Frameworks des Jahres 2025: React, Svelte und Astro. Du bekommst praktische "Hello World"-Beispiele für jedes Framework und klare Entscheidungshilfen, die dir bei der Auswahl helfen. Spoiler: Es gibt kein "bestes" Framework – es kommt auf deinen Use Case an.

Was sind JavaScript-Frameworks?

Bevor wir in den Vergleich einsteigen, lass uns kurz klären, was JavaScript-Frameworks eigentlich sind und warum wir sie brauchen.

Ein JavaScript-Framework ist eine Sammlung von vorgefertigten Code-Bausteinen, die dir helfen, moderne Webanwendungen effizienter zu entwickeln. Statt jedes Mal das Rad neu zu erfinden, bieten Frameworks Lösungen für wiederkehrende Probleme wie:

  • Komponentenbasierte Entwicklung: Teile deine Benutzeroberfläche in wiederverwendbare Bausteine auf
  • Reaktivität: Die Benutzeroberfläche aktualisiert sich automatisch, wenn sich Daten ändern
  • State Management: Verwalte den Zustand deiner Anwendung zentral
  • Routing: Navigation zwischen verschiedenen Seiten ohne volle Seitenladezeiten

Frameworks sparen dir Zeit, helfen dir dabei, sauberen Code zu schreiben, und bieten bewährte Lösungsansätze, die in der Community getestet wurden.

React: Der etablierte Standard

Was ist React?

React wurde 2013 von Facebook (heute Meta) entwickelt und ist heute das mit Abstand populärste JavaScript-Framework. Genau genommen ist React eine Bibliothek (Library) für User Interfaces, wird aber oft als Framework bezeichnet. React setzt auf das Konzept des Virtual DOM – einer virtuellen Darstellung des DOM, die React nutzt, um Updates effizient durchzuführen.

Die Stärken von React

Riesiges Ökosystem: React hat die größte Community aller JavaScript-Frameworks. Das bedeutet:

  • Unzählige Third-Party-Bibliotheken für fast jeden Use Case
  • Viele Tutorials, Kurse und Lernressourcen
  • Schnelle Antworten auf Stack Overflow und in Foren

Job-Markt: Wenn du als Entwickler arbeiten möchtest, ist React die sicherste Wahl. Die meisten Job-Angebote setzen React-Kenntnisse voraus.

Mature und stabil: Nach über 10 Jahren Entwicklung ist React ausgereift. Breaking Changes sind selten, und Meta investiert kontinuierlich in die Weiterentwicklung.

Die Lernkurve

React hat eine mittelschwere Lernkurve für Einsteiger:

  • JSX-Syntax: Du schreibst HTML-ähnlichen Code direkt in JavaScript. Das wirkt anfangs ungewohnt, wird aber schnell zur zweiten Natur.
  • Hooks: Seit React 16.8 gibt es Hooks wie useState und useEffect. Sie ersetzen die alten Class Components und sind einfacher zu verstehen.
  • Konzepte: Du musst Konzepte wie Props, State und Component Lifecycle verstehen.

Praktisches React-Beispiel

Hier ist eine einfache Counter-Komponente in React:

// Einfache Counter-Komponente mit React Hooks
import { useState } from 'react';

function Counter() {
  // useState Hook für reaktiven State
  // count ist die aktuelle Zahl, setCount ist die Funktion zum Ändern
  const [count, setCount] = useState(0);

  return (
    <div>
      <h1>React Counter</h1>
      <p>Aktuelle Zahl: {count}</p>
      <button onClick={() => setCount(count + 1)}>
        Erhöhen
      </button>
      <button onClick={() => setCount(count - 1)}>
        Verringern
      </button>
      <button onClick={() => setCount(0)}>
        Zurücksetzen
      </button>
    </div>
  );
}

export default Counter;

Was passiert hier?

  • useState(0) erstellt eine State-Variable count mit Startwert 0
  • Bei jedem Klick auf "Erhöhen" wird setCount aufgerufen
  • React erkennt die Änderung und aktualisiert automatisch die Anzeige
  • JSX mischt HTML-ähnliche Syntax mit JavaScript ({count} ist JavaScript)

Svelte: Der aufsteigende Stern

Was ist Svelte?

Svelte, entwickelt von Rich Harris, ist das jüngste der drei Frameworks (erstes Release 2016, große Überarbeitung 2019 mit Svelte 3). Der große Unterschied zu React: Svelte ist ein Compiler, kein Framework, das zur Laufzeit im Browser läuft.

Das bedeutet: Svelte verwandelt deinen Code beim Build-Prozess in hochoptimiertes Vanilla JavaScript. Es gibt keine Framework-Runtime, die im Browser geladen werden muss – Svelte "verschwindet" im finalen Bundle.

Die Stärken von Svelte

Einfache, intuitive Syntax: Svelte fühlt sich näher an HTML, CSS und JavaScript an als React. Es gibt weniger Boilerplate-Code.

Performance: Da Svelte zur Build-Zeit kompiliert wird, ist es extrem schnell. Keine Virtual DOM-Berechnungen zur Laufzeit.

Weniger Code: Svelte-Komponenten brauchen im Schnitt 40% weniger Code als vergleichbare React-Komponenten.

Reaktivität "einfach da": In Svelte ist Reaktivität eingebaut – du musst keine speziellen Hooks oder State-Management-Tools lernen.

Die Lernkurve

Svelte hat die niedrigste Lernkurve der drei Frameworks:

  • HTML-ähnlicher als JSX: Svelte-Templates sehen aus wie HTML mit einigen Erweiterungen
  • Reaktivität ist intuitiv: let count = 0; count += 1 funktioniert einfach
  • Scoped CSS: Styles sind automatisch auf die Komponente beschränkt

Praktisches Svelte-Beispiel

Hier ist die gleiche Counter-Komponente in Svelte:

<script>
  // Reaktive Variable - automatisch aktualisiert
  let count = 0;

  // Reaktive Berechnungen mit $:
  // Wird automatisch neu berechnet, wenn count sich ändert
  $: doubled = count * 2;

  // Funktionen für Button-Clicks
  function increment() {
    count += 1; // So einfach ist Reaktivität in Svelte!
  }

  function decrement() {
    count -= 1;
  }

  function reset() {
    count = 0;
  }
</script>

<div>
  <h1>Svelte Counter</h1>
  <p>Aktuelle Zahl: {count}</p>
  <p>Verdoppelt: {doubled}</p>

  <button on:click={increment}>
    Erhöhen
  </button>
  <button on:click={decrement}>
    Verringern
  </button>
  <button on:click={reset}>
    Zurücksetzen
  </button>
</div>

<style>
  /* Scoped CSS - gilt nur für diese Komponente */
  div {
    padding: 20px;
    background-color: #f0f0f0;
    border-radius: 8px;
  }

  button {
    margin: 5px;
    padding: 10px 15px;
    background-color: #ff3e00;
    color: white;
    border: none;
    border-radius: 4px;
    cursor: pointer;
  }

  button:hover {
    background-color: #cc3100;
  }
</style>

Was passiert hier?

  • let count = 0 ist eine normale Variable – Svelte macht sie automatisch reaktiv
  • $: doubled = count * 2 ist eine reaktive Berechnung
  • on:click={increment} bindet die Funktion an den Button
  • CSS im <style> Block gilt nur für diese Komponente
  • Deutlich weniger Code als React, aber gleiche Funktionalität!

Astro: Der Performance-Champion

Was ist Astro?

Astro ist das neueste Framework in diesem Vergleich (erstes Release 2021) und verfolgt einen radikal anderen Ansatz: Zero JavaScript by Default. Astro ist primär ein Static Site Generator mit einer innovativen Architektur namens "Islands Architecture".

Die Grundidee: Deine Website ist standardmäßig statisches HTML und CSS – komplett ohne JavaScript. Nur dort, wo du Interaktivität brauchst, fügst du "Islands" (Inseln) von JavaScript hinzu.

Die Stärken von Astro

Extrem schnell: Da Astro standardmäßig kein JavaScript ausliefert, sind Astro-Sites blitzschnell. Perfekte Lighthouse-Scores sind die Regel, nicht die Ausnahme.

Content-fokussiert: Astro wurde für Content-Sites wie Blogs, Marketing-Websites und Dokumentationen entwickelt. Weniger für hochinteraktive Web-Apps.

Multi-Framework Support: Das Besondere an Astro: Du kannst React, Svelte, Vue und andere Frameworks gleichzeitig in einem Projekt nutzen. Jede Komponente kann in einem anderen Framework geschrieben sein.

Developer Experience: Astro bietet eine hervorragende Entwickler-Erfahrung mit schnellem Hot Reload und klarer Fehlermeldungen.

Die Lernkurve

Astro hat eine mittlere Lernkurve:

  • Ähnlich wie andere Komponenten-Frameworks: Wenn du React oder Svelte kennst, findest du dich schnell zurecht
  • Neues Konzept: "Islands Architecture" ist ein neues Paradigma, das du verstehen musst
  • Zwei "Modi": Code im Frontmatter (--- Bereich) läuft beim Build, Code in <script> Tags läuft im Browser

Praktisches Astro-Beispiel

Hier ist die Counter-Komponente in Astro:

---
// Component Script (läuft nur beim Build)
const title = "Astro Counter";
const currentDate = new Date().toLocaleDateString('de-DE');

// Dieser Code läuft auf dem Server, nicht im Browser!
console.log('Diese Komponente wird gerade gebaut...');
---

<div class="counter-container">
  <h1>{title}</h1>
  <p class="date">Erstellt am: {currentDate}</p>

  <!-- Interaktive Island mit Client-JavaScript -->
  <!-- client:load bedeutet: JavaScript beim Laden ausführen -->
  <div id="counter">
    <p>Aktuelle Zahl: <span id="count">0</span></p>
    <button id="increment">Erhöhen</button>
    <button id="decrement">Verringern</button>
    <button id="reset">Zurücksetzen</button>
  </div>
</div>

<script>
  // Dieser Code läuft im Browser (Client-Side)
  let count = 0;

  // DOM-Elemente holen
  const incrementBtn = document.getElementById('increment');
  const decrementBtn = document.getElementById('decrement');
  const resetBtn = document.getElementById('reset');
  const countDisplay = document.getElementById('count');

  // Event Listeners hinzufügen
  incrementBtn?.addEventListener('click', () => {
    count++;
    updateDisplay();
  });

  decrementBtn?.addEventListener('click', () => {
    count--;
    updateDisplay();
  });

  resetBtn?.addEventListener('click', () => {
    count = 0;
    updateDisplay();
  });

  // Display aktualisieren
  function updateDisplay() {
    if (countDisplay) {
      countDisplay.textContent = count.toString();
    }
  }
</script>

<style>
  .counter-container {
    padding: 20px;
    background-color: #f9f9f9;
    border-radius: 8px;
    max-width: 400px;
    margin: 20px auto;
  }

  .date {
    color: #666;
    font-size: 0.9em;
  }

  #counter {
    margin-top: 20px;
    padding: 15px;
    background-color: white;
    border-radius: 4px;
    box-shadow: 0 2px 4px rgba(0,0,0,0.1);
  }

  button {
    margin: 5px;
    padding: 10px 15px;
    background-color: #5b21b6;
    color: white;
    border: none;
    border-radius: 4px;
    cursor: pointer;
    font-weight: 500;
  }

  button:hover {
    background-color: #4c1d95;
  }

  #count {
    font-weight: bold;
    font-size: 1.2em;
    color: #5b21b6;
  }
</style>

Was passiert hier?

  • Code im --- Bereich läuft beim Build (Server-Side)
  • currentDate wird zur Build-Zeit erstellt, nicht beim Seitenaufruf
  • HTML wird als statischer Content ausgeliefert
  • Nur der Code im <script> Tag läuft im Browser
  • Weniger "magisch" als React/Svelte, aber volle Kontrolle

Hinweis: In der Praxis würdest du für interaktive Komponenten in Astro oft React, Svelte oder Vue nutzen. Das obige Beispiel zeigt Vanilla JavaScript, um die Islands-Idee zu verdeutlichen.

Direkter Vergleich: Die wichtigsten Unterschiede

Hier ist eine Übersicht, die dir bei der Entscheidung hilft:

Kriterium React Svelte Astro
Lernkurve Mittel Niedrig Mittel
Performance Gut Sehr gut Hervorragend
Bundle Size ~40-50 KB (gzipped) ~2-3 KB (gzipped) 0 KB (Standard)*
Ökosystem Riesig Wachsend Klein, aber wachsend
Job-Markt Sehr groß Klein, aber wachsend Sehr klein
Beste für SPAs, komplexe Apps Moderne Web-Apps Content-Sites, Blogs
Community Sehr groß Groß Mittel
Learning Resources Sehr viele Viele Wachsend
Entwicklungsgeschwindigkeit Gut Sehr gut Gut
Reaktivität Hooks Eingebaut Manuell (oder via Framework-Integration)

*Astro liefert standardmäßig kein JavaScript aus. JavaScript wird nur für interaktive "Islands" geladen.

Bundle Size erklärt

Die Zahlen bedeuten, wie viel Framework-Code im Browser geladen werden muss:

  • React: ~42 KB (React + ReactDOM, gzipped)
  • Svelte: ~2 KB – fast nichts, da Svelte beim Build kompiliert wird
  • Astro: 0 KB – statische Sites brauchen kein Framework im Browser

Das ist besonders wichtig für:

  • Mobile Nutzer mit langsamen Verbindungen
  • SEO (Google bevorzugt schnelle Websites)
  • User Experience (schnellere Ladezeiten)

Welches Framework für wen?

Hier sind meine Empfehlungen, basierend auf verschiedenen Szenarien:

Wähle React, wenn du...

Jobs als Frontend-Entwickler suchst: React ist mit Abstand am gefragtesten
Ein großes Ökosystem brauchst: Für jedes Problem gibt es eine React-Library
Komplexe Single-Page Applications (SPAs) baust: React ist perfekt für Web-Apps wie Dashboards, SaaS-Tools
Mit einem etablierten, ausgereiften Tool arbeiten möchtest: React ist "battle-tested"
Teil einer großen Community sein willst: Hilfe ist überall verfügbar

Nicht ideal für: Einfache Websites, Performance-kritische Content-Sites

Wähle Svelte, wenn du...

Einfache, intuitive Syntax magst: Svelte ist am schnellsten zu lernen
Performance wichtig ist: Svelte ist extrem schnell
Weniger Code schreiben willst: Svelte ist prägnant und ausdrucksstark
Moderne Entwicklung magst: Svelte fühlt sich "2025" an
Kleinere bis mittlere Web-Apps baust: Svelte glänzt hier

Nicht ideal für: Wenn du sofort einen Job brauchst (noch nicht so verbreitet)

Wähle Astro, wenn du...

Content-Sites baust: Blogs, Marketing-Websites, Dokumentationen, Portfolios
Performance oberste Priorität ist: Astro ist unschlagbar schnell
Wenig JavaScript brauchst: Die meiste Site ist statisch
SEO wichtig ist: Statische Sites sind perfekt für Suchmaschinen
Flexibilität willst: Du kannst React, Svelte, Vue mischen

Nicht ideal für: Hochinteraktive SPAs, Echtzeit-Apps, komplexe Web-Apps

Mein persönlicher Tipp für Einsteiger

Wenn du gerade erst anfängst, würde ich in dieser Reihenfolge lernen:

  1. Starte mit Svelte – Die niedrigste Lernkurve, und du verstehst Grundkonzepte schnell
  2. Lerne dann React – Für Job-Chancen und das große Ökosystem
  3. Entdecke Astro – Wenn du Content-Sites bauen willst

Du musst nicht alle drei können! Wähle eines und werde richtig gut darin.

Erste Schritte: So startest du

Hier ist, wie du mit jedem Framework ein erstes Projekt erstellst:

React: Projekt starten

# Mit Vite (empfohlen – schneller als Create React App)
npm create vite@latest mein-react-projekt -- --template react

# In das Projektverzeichnis wechseln
cd mein-react-projekt

# Dependencies installieren
npm install

# Entwicklungsserver starten
npm run dev

Der Dev-Server läuft dann auf http://localhost:5173

Svelte: Projekt starten

# Mit SvelteKit (empfohlen)
npm create svelte@latest mein-svelte-projekt

# Wähle: Skeleton project ? Yes, using TypeScript syntax (optional) ? Add Prettier

# In das Projektverzeichnis wechseln
cd mein-svelte-projekt

# Dependencies installieren
npm install

# Entwicklungsserver starten
npm run dev

Der Dev-Server läuft dann auf http://localhost:5173

Astro: Projekt starten

# Mit dem Astro CLI
npm create astro@latest mein-astro-projekt

# Wähle: Empty ? Yes (TypeScript optional) ? Install dependencies: Yes

# In das Projektverzeichnis wechseln
cd mein-astro-projekt

# Entwicklungsserver starten
npm run dev

Der Dev-Server läuft dann auf http://localhost:4321

VS Code Extensions

Installiere diese Extensions für bessere Developer Experience:

Für React:

  • ES7+ React/Redux/React-Native snippets
  • Prettier – Code formatter

Für Svelte:

  • Svelte for VS Code (offiziell)
  • Svelte Intellisense

Für Astro:

  • Astro (offiziell)
  • Prettier (mit Astro Plugin)

Browser-Kompatibilität & Voraussetzungen

Node.js Version

Alle drei Frameworks benötigen Node.js:

  • Minimal: Node.js 18.x
  • Empfohlen: Node.js 20.x oder 22.x (LTS)

Download: nodejs.org

Browser-Support

React:

  • Alle modernen Browser (Chrome, Firefox, Safari, Edge)
  • Internet Explorer 11 wird nicht mehr unterstützt

Svelte:

  • Alle modernen Browser
  • Sehr guter Support bis zurück zu Safari 12

Astro:

  • Alle modernen Browser
  • Da Astro statisches HTML generiert, funktioniert die Basis-Site sogar ohne JavaScript

Empfohlene Entwicklungsumgebung

  • Editor: VS Code, WebStorm oder Cursor
  • Terminal: Integriertes Terminal oder iTerm2 (Mac) / Windows Terminal
  • Browser: Chrome oder Firefox (mit DevTools)
  • Git: Für Versionskontrolle

Zusammenfassung

Hier sind die wichtigsten Punkte im Überblick:

  • Es gibt kein "bestes" Framework – die richtige Wahl hängt von deinem Use Case ab
  • React ist der etablierte Standard mit riesigem Ökosystem und den meisten Job-Möglichkeiten
  • Svelte ist modern, einfach zu lernen und extrem performant – perfekt für Einsteiger
  • Astro ist ideal für Content-Sites und bietet unschlagbare Performance durch statische HTML-Generierung
  • Alle drei sind 2025 gute Wahlen und werden aktiv weiterentwickelt
  • Fokussiere dich auf eines und lerne es richtig, statt alle oberflächlich anzukratzen
  • Die Grundlagen sind wichtiger als das Framework – HTML, CSS und JavaScript sind das Fundament

Meine Empfehlung

Für deinen ersten Einstieg: Probiere Svelte aus! Es hat die niedrigste Lernkurve und zeigt dir moderne Konzepte ohne viel Komplexität.

Wenn du als Entwickler arbeiten willst: Investiere Zeit in React. Es öffnet die meisten Türen.

Wenn du einen Blog oder eine Marketing-Site baust: Schau dir Astro an. Du wirst die Performance lieben.

Weiterführende Ressourcen

Offizielle Dokumentationen

Interactive Learning

Community & News

  • State of JavaScript Survey: stateofjs.com – Jährliche Entwickler-Umfrage
  • JavaScript Weekly: javascriptweekly.com – Newsletter mit News
  • Dev.to: Viele Tutorials und Erfahrungsberichte zu allen drei Frameworks

Eine smarte ZSH-Funktion: cat für Dateien und URLs

Als Unix/Linux-Nutzer kennen wir alle den beliebten 'cat' Befehl, um Dateiinhalte anzuzeigen. Aber was, wenn wir den gleichen, vertrauten Befehl auch für URLs verwenden möchten? Hier ist eine elegante Lösung in Form einer ZSH-Funktion.

Die folgende Funktion erweitert den klassischen 'cat' Befehl so, dass er sowohl mit lokalen Dateien als auch mit URLs umgehen kann:

Die Funktion prüft mittels eines regulären Ausdrucks, ob das übergebene Argument eine URL ist. Falls ja, wird curl verwendet, um den Inhalt herunterzuladen und anzuzeigen. Bei normalen Dateien wird der Standard-cat-Befehl verwendet.

Beispiele für die Verwendung:

  • cat datei.txt -> Zeigt den Inhalt einer lokalen Datei
  • cat https://github.com/beispiel -> Zeigt den HTML-Inhalt der Website

Die Funktion könnt ihr einfach in eure ~/.zshrc einfügen und nach dem Neuladen der Shell sofort nutzen.

Ein kleiner, aber feiner Hack, der den Workflow beim Arbeiten mit Dateien und Webinhalten vereinfacht. Das ist Unix-Philosophie at its best: Bestehende Werkzeuge erweitern, ohne ihre ursprüngliche Funktionalität zu beeinträchtigen.

a dad handing a rubber chicken to his daughter

Mein neues Zufalls-„Dad Joke“ Ruby Gem: ICanHazDadJoke

Hallo zusammen!

Ich freue mich, euch heute mein neuestes Ruby Gem vorzustellen: icanhazdadjoke. Wie der Name schon verrät, ermöglicht dieses Gem euch, auf einfache Weise zufällige "Dad Jokes" aus der icanhazdadjoke API abzurufen. Wer liebt nicht einen guten, alten, schlechten Witz?

Was ist ein "Dad Joke"?

"Dad Jokes" sind einfache, oft vorhersehbare und klischeehafte Witze, die typischerweise von Vätern erzählt werden. Sie zeichnen sich durch ihre Wortspiele und ihren harmlosen Humor aus. Beispiele gefällig? Hier sind ein paar Klassiker:

  • Warum können Geister so schlecht lügen? Weil man sie durchschauen kann!
  • Wie nennt man einen Bumerang, der nicht zurückkommt? Einen Stock!

Ha! Ich weiß.

Funktionen des Gems

Mit icanhazdadjoke könnt ihr ganz einfach zufällige Witze abrufen und in euren Ruby-Anwendungen verwenden oder direkt im Terminal aufrufen. Das Gem ist leicht zu installieren und noch einfacher zu benutzen. Hier sind einige der Hauptfunktionen:

  • Zufällige Witze abrufen: Holt euch einen zufälligen Witz mit einer einzigen Zeile Code.
  • Leicht erweiterbar: Das Gem ist so konzipiert, dass es leicht erweitert und in bestehende Projekte integriert werden kann.

Installation

Die Installation des Gems ist einfach und dauert nur einen Moment. Führt einfach folgenden Befehl in eurem Terminal aus:

gem install icanhazdadjoke

Nutzung

Nachdem ihr das Gem installiert habt, könnt ihr es in eurem Projekt verwenden. Hier ist ein kurzes Beispiel, wie ihr einen zufälligen Witz abrufen könnt:

require 'icanhazdadjoke'

joke = ICanHazDadJoke.fetch_joke
puts joke

So einfach ist das! Ihr könnt den abgerufenen Witz dann überall in eurem Code verwenden, wo ihr ihn braucht.

Beispielprojekt

Hier ist ein kleines Beispielprojekt, das zeigt, wie man das Gem in einer einfachen Ruby-Anwendung verwenden kann:

require 'sinatra'
require 'icanhazdadjoke'

get '/' do
  joke = ICanHazDadJoke.fetch_joke
  "<h1>Dad Joke</h1><p>#{joke}</p>"
end

Dieses einfache Sinatra-Projekt erstellt einen kleinen Webserver, der bei jedem Aufruf der Startseite einen neuen Witz anzeigt.

Fazit

Ich hoffe, euch gefällt mein neues Gem icanhazdadjoke. Es war ein lustiges Projekt und ich freue mich darauf zu sehen, wie ihr es in euren eigenen Projekten verwendet. Wenn ihr Fragen oder Anregungen habt, lasst es mich gerne wissen. Den Code und weitere Informationen findet ihr auf GitHub.

Viel Spaß beim Programmieren und vergesst nicht, hin und wieder einen guten "Dad Joke" zu erzählen!

PHP Scripte regelmäßig per cronjob ausführen lassen

Heute möchte ich euch kurz zeigen, wie man PHP Scripte, die über einen Browser ausgeführt werden müssen, auch per Cronjob z.B. täglich ausführen lassen kann.

Nehmen wir an, wir haben ein PHP Script, dass über die folgende URL aufgerufen wird. Nehmen wir beispielsweise an, dieses Script leert den Cache unserer Anwendung. Die URL für das Script könnte demnach folgendermaßen aussehen:

http://www.meineseite.de/scripte/delete_cache.php

Um also den Cache zu leeren, können wir diese URL einfach im Browser aufrufen. Doch was machen wir, wenn wir das Script nun jeden Tag um 12:00 Uhr ausführen wollen?

Den Cron-Daemon nutzen

Wenn wir das Glück haben, per SSH auf den Server zugreifen zu können und möglicherweise sogar noch root Rechte besitzen, können wir hierfür einen Cronjob für den Cron-Daemon erstellen. Was genau der Cron-Daemon ist, erfahrt ihr hier:

"Der Cron-Daemon ist ein Dienst, der automatisch Skripte und Programme zu vorgegebenen Zeiten, also chronologisch, starten kann. Der auszuführende Befehl wird in einer Tabelle, der crontab, gespeichert. Es gibt systemweite Crontabs, die nur mit root-Rechten bearbeitet werden können, zusätzlich kann jeder Benutzer seine eigene Crontab erstellen."
Source: http://wiki.ubuntuusers.de/Cron

Das bedeutet also, dass wir die Tabelle mit den Cronjob Befehlen bearbeiten müssen und unser Script dort eintragen. Unter Ubuntu funktioniert dies so.

crontab -e

Weiterlesen →

Ruby on Rails auf Ubuntu 11.10 installieren

Seit einigen Tagen (Oktober 2011) ist die neuste Version von Ubuntu zum Download freigegeben. Ubuntu 11.10 oder auch Oneiric Ocelot genannt, ist ein wirklich ausgereiftes und leicht zu bedienendes Linux Betriebssystem, welches kostenlos zum Download bereit steht. Da Linux schon von Natur aus viele Funktionen und Helfer für Programmierer bereit hält, eignet es sich auch hervorragend zur Entwicklung von Ruby on Rails Anwendungen.

Ruby on Rails hingegen ist derzeit eines der populärsten und produktivsten OpenSource Frameworks, mit dem sich sehr schnell umfangreiche Webanwendungen erstellen lassen. Wer schon damit gearbeitet hat, weiß wieviel Spaß es machen kann.

Ruby selbst ist eine Scriptsprache, wie z.B. auch PHP eine Scriptsprache ist. Ruby wurde jedoch von Grund auf mit dem Ziel entwickelt, leicht verständlich zu sein und eine schnelle Entwicklung damit zu ermöglichen.

Ubuntu für Rails vorbereiten

Damit wir das Rails Framework unter Ubuntu 11.10 verwenden können, müssen wir zuerst Ruby installieren. Von Haus aus ist Ubuntu noch nicht mit Ruby ausgestattet, jedoch läßt sich dieses (und auch andere Programmiersprachen) sehr leicht nachträglich installieren.

Hierzu muss man wissen, dass es verschiedene Versionen von Ruby gibt. Ältere Versionen von Ruby on Rails haben hauptsächlich auf die Ruby Version 1.8.7 gesetzt. Die neusten Ruby on Rails Versionen ab 3.0 verwenden jedoch bevorzugt Ruby Version 1.9.2. Um unser System also für die Zukunft vorzubereiten, werden wir auf den Ruby Versions Manager (RVM) setzen, mit dem es möglich ist jede beliebige Ruby Version zu installieren und auch bequem zwischen diesen zu wechseln.

Weiterlesen →