Skip to main content

Änderungen, Versionen und Projektgeschichte

Änderungen sind die Rohstoffe der Entwicklung

Software entsteht nicht in einem einzigen Schritt. Sie entwickelt sich fortlaufend:

  • Eine Funktion wird ergänzt.
  • Ein Fehler wird korrigiert.
  • Eine Abhängigkeit wird aktualisiert.
  • Eine Konfiguration wird angepasst.
  • Dokumentation wird verbessert.
  • Code wird umstrukturiert, ohne sein Verhalten zu ändern.

Jede dieser Anpassungen ist eine Änderung. Für sich genommen kann sie klein sein; über Wochen, Monate und Jahre bilden Änderungen jedoch die Geschichte eines Projekts.

Ohne Versionskontrolle bleibt diese Geschichte meist unsichtbar. Dateien werden überschrieben, Kopien angelegt oder Änderungen nur mündlich kommuniziert. Das funktioniert kurzzeitig, wird aber schnell unsicher.

Stell dir vor, eine Datei Calculator.php wird geändert:

public function add(int $a, int $b): int
{
    return $a + $b;
}

Später ergänzt jemand eine Eingabeprüfung:

public function add(int $a, int $b): int
{
    if ($a < 0 || $b < 0) {
        throw new InvalidArgumentException('Negative Werte sind nicht erlaubt.');
    }

    return $a + $b;
}

Die zweite Variante ist nicht einfach „die Datei“. Sie ist eine neue Fassung derselben Datei. Für ein einzelnes Beispiel ist der Unterschied offensichtlich. In einem Projekt mit hunderten Dateien, mehreren Entwicklerinnen und Entwicklern und vielen parallelen Aufgaben ist er ohne Werkzeug kaum zuverlässig nachzuvollziehen.


Was eine Version bedeutet

Eine Version ist ein eindeutig nachvollziehbarer Zustand eines Projekts zu einem bestimmten Zeitpunkt.

Dabei geht es nicht nur um eine einzelne Datei. Eine Version kann den gemeinsamen Zustand vieler Bestandteile beschreiben:

  • PHP-Quellcode,
  • Tests,
  • Composer-Konfiguration,
  • Dokumentation,
  • Konfigurationsdateien,
  • CI-Workflows,
  • Datenbankschemata,
  • Build-Skripte.

Eine Version beantwortet Fragen wie:

  • Welcher Code war letzte Woche produktiv?
  • Wann wurde dieser Fehler eingeführt?
  • Welche Dateien gehörten zu Version 2.1.0?
  • Welche Änderung hat ein Verhalten verändert?
  • Wer hat diese Anpassung vorgenommen und warum?
  • Wie kann ein früherer funktionierender Stand wiederhergestellt werden?

Eine Versionsnummer wie 1.4.2 ist dabei eine fachliche Kennzeichnung, etwa für ein Release. Git selbst verwaltet dagegen technische Zustände über Commits und eindeutige Kennungen. Auf diesen Unterschied wird der Kurs später noch detailliert eingehen.

Wichtig: Eine Version ist nicht zwingend eine veröffentlichte Produktversion. Auch jeder kleine, lokale Entwicklungsschritt kann eine nachvollziehbare Version sein.


Von Dateikopien zur echten Projektgeschichte

Ohne Versionskontrolle sieht die Entwicklung eines Projekts oft ungefähr so aus:

projekt/
├── index.php
├── index_alt.php
├── index_neu.php
├── index_final.php
├── index_final_final.php
└── index_final_wirklich_final.php

Oder es entstehen Ordner wie:

shopprojekt/
├── backup/
├── backup_neu/
├── backup_vor_update/
├── stand_vom_freitag/
└── produktiv_final/

Diese Methode hat erkennbare Nachteile:

  1. Unklare Herkunft
    Es ist nicht mehr klar, welche Kopie die richtige ist.

  2. Keine Begründung
    Dateinamen erklären selten, warum etwas geändert wurde.

  3. Schwierige Vergleiche
    Unterschiede zwischen zwei Ordnern oder Dateien müssen mühsam gesucht werden.

  4. Keine sichere Zusammenarbeit
    Mehrere Personen überschreiben leicht gegenseitig ihre Änderungen.

  5. Unvollständige Sicherung
    Häufig werden nur einzelne Dateien kopiert. Zusammenhängende Änderungen fehlen dann.

  6. Keine saubere Rückkehr
    Ein alter Stand kann vielleicht gefunden werden, aber nicht zuverlässig wiederhergestellt werden.

Git ersetzt diese unstrukturierten Kopien durch eine präzise Projektgeschichte. Statt Dateien mit Zusätzen wie _alt oder _neu zu speichern, hält Git fest:

  • welche Dateien geändert wurden,
  • was sich darin geändert hat,
  • wann die Änderung gespeichert wurde,
  • wer sie erstellt hat,
  • warum sie vorgenommen wurde,
  • und auf welchem vorherigen Stand sie aufbaut.

Änderungen als nachvollziehbare Einheiten

In professionellen Projekten wird nicht jede Tastatureingabe sofort als dauerhafte Version gespeichert. Stattdessen fasst man logisch zusammenhängende Änderungen zu einer Einheit zusammen.

Ein Beispiel: Für eine neue Passwortprüfung könnten diese Änderungen zusammengehören:

  • PasswordValidator.php wird ergänzt,
  • passende Unit-Tests werden erstellt,
  • eine Fehlermeldung wird dokumentiert,
  • eine Konfiguration wird angepasst.

Diese gemeinsame Änderung sollte später als eine nachvollziehbare Einheit sichtbar sein. In Git heißt eine solche gespeicherte Einheit Commit.

Eine passende Beschreibung könnte lauten:

feat(auth): Passwortlänge validieren

Nicht hilfreich wären dagegen Nachrichten wie:

Update
Änderungen
Fix

Die Qualität einer Projektgeschichte hängt wesentlich davon ab, ob Änderungen sinnvoll zusammengefasst und verständlich beschrieben werden.

Gute Änderungseinheit

Eine gute Änderungseinheit beantwortet idealerweise eine konkrete Frage:

„Was wurde mit diesem Schritt erreicht?“

Beispiele:

  • „E-Mail-Adresse bei der Registrierung validieren“
  • „Fehlerhafte Berechnung der Mehrwertsteuer korrigieren“
  • „PHPUnit auf Version 11 aktualisieren“
  • „README um lokale Installationsschritte ergänzen“
  • „Nicht verwendete Legacy-Klasse entfernen“

Zu große Änderungseinheit

Folgende Mischung ist problematisch:

Neue Login-Funktion, CSS-Anpassungen, Composer-Update,
Tippfehlerkorrekturen und Datenbank-Migration

Diese Änderung enthält mehrere unabhängige Themen. Wenn später ein Fehler in der Login-Funktion auftritt, möchte man nicht gleichzeitig CSS-Anpassungen oder ein Abhängigkeitsupdate zurücknehmen müssen.

Zu kleine Änderungseinheit

Auch das Gegenteil kann unpraktisch sein:

Leerzeile in Datei A ergänzt
Leerzeile in Datei B ergänzt
Leerzeile in Datei C ergänzt

Nicht jede minimale Änderung braucht einen eigenen Commit. Entscheidend ist, dass die Historie fachlich verständlich bleibt.


Projektgeschichte als Kette von Entscheidungen

Eine Projektgeschichte ist mehr als eine Liste technischer Dateiunterschiede. Sie dokumentiert Entscheidungen.

Angenommen, ein Projekt entwickelt sich in diesen Schritten:

A  Projektgrundstruktur anlegen
B  Composer-Abhängigkeiten hinzufügen
C  Benutzerregistrierung implementieren
D  E-Mail-Validierung ergänzen
E  Validierungsfehler korrigieren
F  Version 1.0.0 veröffentlichen

Diese Abfolge erklärt nicht nur, dass sich Dateien verändert haben. Sie zeigt auch, wie das Projekt zu seinem aktuellen Zustand gelangt ist.

Vereinfacht kann man die Entwicklung als Kette darstellen:

A ── B ── C ── D ── E ── F

Jeder Schritt baut auf einem vorherigen Stand auf. Die aktuelle Version enthält also die Ergebnisse aller vorherigen Schritte.

Das ist ein wichtiges Denkmodell:

Die Projektgeschichte ist keine Ansammlung zufälliger Sicherungskopien, sondern eine Folge bewusst gespeicherter Zustände und Entscheidungen.

Später können sich solche Entwicklungslinien aufteilen und wieder zusammengeführt werden. Git verwendet dafür Branches und Merges. Zunächst genügt die Vorstellung einer linearen Geschichte.


Momentaufnahme und Änderung unterscheiden

Im Alltag sprechen Teams oft davon, „eine Änderung zu speichern“. Git arbeitet intern jedoch vor allem mit Momentaufnahmen des Projektzustands.

Bei einem Commit hält Git fest, wie die verfolgten Dateien zu diesem Zeitpunkt aussehen. Der Commit verweist außerdem auf seinen Vorgänger. Dadurch kann Git Unterschiede zwischen Zuständen berechnen.

Vereinfacht:

Commit A: Grundstruktur
Commit B: Grundstruktur + Composer-Dateien
Commit C: Grundstruktur + Composer-Dateien + Registrierung

Git muss nicht für jede Version eine vollständige Ordnerkopie neben die vorherige legen. Es speichert Inhalte effizient und organisiert sie so, dass frühere Zustände weiterhin erreichbar bleiben.

Für die praktische Arbeit ist zunächst diese Formulierung hilfreich:

  • Eine Änderung beschreibt, was du bearbeitet hast.
  • Ein Commit speichert einen nachvollziehbaren Projektzustand.
  • Die Historie verbindet diese Zustände miteinander.

Warum die Geschichte im Alltag wichtig ist

Eine gute Historie hilft nicht nur bei großen Problemen. Sie ist ein tägliches Arbeitswerkzeug.

Fehlerursachen finden

Ein Test schlägt plötzlich fehl. Nun stellt sich die Frage:

„Seit wann ist dieses Verhalten vorhanden?“

Mit der Historie kannst du prüfen:

  • Welche Änderungen wurden zuletzt vorgenommen?
  • Welche Datei wurde verändert?
  • In welchem Commit wurde die betreffende Zeile eingeführt?
  • Welche Begründung stand in der Commit-Nachricht?

Statt zu raten, untersuchst du konkrete Informationen.

Frühere Zustände vergleichen

Vielleicht funktioniert ein Formular in der aktuellen Version nicht mehr, in der Vorversion aber schon. Dann vergleichst du beide Stände.

Beispielhaft:

Version vorher: Formular sendet Daten korrekt
Version aktuell: Validierung verhindert das Absenden

Ein Vergleich zeigt, welche Codezeilen zwischen diesen Zuständen verändert wurden.

Änderungen gezielt zurücknehmen

Nicht jede Änderung soll dauerhaft bleiben. Vielleicht wurde eine Funktion falsch umgesetzt oder ein Update verursacht Probleme.

Ohne Versionskontrolle müsstest du alte Dateien suchen und manuell zurückkopieren. Mit Git kannst du gezielt feststellen:

  • welcher Commit problematisch ist,
  • welche Dateien er verändert hat,
  • und wie die Änderung kontrolliert zurückgenommen werden kann.

Wissen im Team erhalten

Menschen wechseln Projekte, vergessen Details oder sind im Urlaub. Eine verständliche Historie bewahrt Kontext.

Statt jemanden fragen zu müssen, warum ein ungewöhnlicher Codeabschnitt existiert, kann die Historie Hinweise liefern:

fix(api): Timeout für langsame Zahlungsanbieter erhöhen

Vielleicht wurde diese Anpassung wegen eines konkreten Produktionsproblems vorgenommen. Eine gute Commit-Nachricht, ein verknüpftes Issue oder ein Pull Request machen diese Entscheidung später nachvollziehbar.

Releases reproduzieren

Wenn ein Kunde einen Fehler in Version 1.8.0 meldet, muss das Team genau diesen Stand untersuchen können — nicht nur die heutige, bereits weiterentwickelte Variante.

Versionskontrolle ermöglicht es, ältere Stände gezielt zu prüfen, Tests auszuführen und bei Bedarf eine Korrektur auf dieser Basis zu entwickeln.


Die Zeitachse eines kleinen Projekts

Ein einfaches PHP-Projekt könnte sich so entwickeln:

1. Projektstruktur erstellen
2. Composer konfigurieren
3. Erste Klasse schreiben
4. Tests hinzufügen
5. Fehler korrigieren
6. Dokumentation ergänzen
7. Release veröffentlichen

Als Historie könnte das aussehen:

a1b2c3d  Projektstruktur anlegen
b2c3d4e  Composer und Autoloading konfigurieren
c3d4e5f  Preisberechnung implementieren
d4e5f6a  Tests für Preisberechnung ergänzen
e5f6a7b  Rundungsfehler bei Mehrwertsteuer korrigieren
f6a7b8c  README um Installationsanleitung erweitern
g7b8c9d  Release 1.0.0 vorbereiten

Die Zeichenfolgen am Anfang stehen beispielhaft für Commit-IDs. Git verwendet sie, um jeden gespeicherten Zustand eindeutig zu identifizieren.

An dieser Liste lässt sich bereits viel ablesen:

  • Die Preisberechnung wurde vor den Tests implementiert.
  • Danach wurde ein Rundungsfehler entdeckt und behoben.
  • Die Dokumentation wurde erst später ergänzt.
  • Das Release baut auf allen vorherigen Änderungen auf.

Eine solche Historie ist wesentlich wertvoller als ein Ordner mit dem Namen projekt_neu_final.


Änderungen haben Kontext

Eine Zeile Code allein erklärt selten genug. Der Kontext einer Änderung ist oft mindestens genauso wichtig wie der technische Unterschied.

Betrachte diese Änderung:

private const TAX_RATE = 0.19;

Wird daraus später:

private const TAX_RATE = 0.20;

ist der technische Unterschied klein. Die entscheidende Frage lautet aber:

Warum wurde der Wert geändert?

Mögliche Antworten:

  • Eine gesetzliche Änderung tritt zu einem bestimmten Datum in Kraft.
  • Der Wert war bisher fehlerhaft.
  • Die Konstante wird nur in einer Testumgebung verwendet.
  • Eine neue Konfiguration ersetzt den festen Wert.

Versionskontrolle kann diesen Kontext über Commit-Nachrichten, Issues, Pull Requests und Reviews festhalten. Git speichert primär die technische Historie; Plattformen wie GitHub ergänzen sie um Diskussionen, Aufgaben und Freigaben.


Die Projektgeschichte ist kein Ersatz für Kommunikation

Eine Commit-Historie ist wertvoll, aber sie ersetzt nicht alles.

Sie kann dokumentieren:

  • was geändert wurde,
  • wann es geschah,
  • wer die Änderung gespeichert hat,
  • welche Nachricht dazu hinterlegt wurde.

Sie kann jedoch nicht automatisch alle fachlichen Hintergründe erfassen. Wichtige Architekturentscheidungen, Sicherheitsannahmen oder Produktentscheidungen gehören häufig zusätzlich in:

  • Issues,
  • Pull-Request-Beschreibungen,
  • Architekturentscheidungsprotokolle,
  • technische Dokumentation,
  • Ticketsysteme,
  • das Projekt-README.

Eine gute Praxis ist daher:

  1. Commits klein und verständlich halten.
  2. Commit-Nachrichten konkret formulieren.
  3. Größere Entscheidungen dokumentieren.
  4. Zusammenhänge zwischen Code, Issue und Pull Request verknüpfen.

Eine Geschichte darf nicht beliebig sein

Nicht jede Projektgeschichte ist automatisch gut, nur weil Git verwendet wird. Auch ein Git-Repository kann unübersichtlich werden.

Typische Warnzeichen sind:

  • Commit-Nachrichten wie „WIP“, „Test“, „Änderung“ oder „asdf“,
  • riesige Commits mit vielen unabhängigen Themen,
  • fehlende Tests bei funktionalen Änderungen,
  • Zugangsdaten oder generierte Dateien in der Historie,
  • unklare oder widersprüchliche Branch-Namen,
  • häufiges Überschreiben bereits veröffentlichter Historie.

Ein Ziel professioneller Versionskontrolle lautet deshalb:

Die Historie soll nicht nur vollständig, sondern auch verständlich, überprüfbar und sicher sein.

Das bedeutet nicht, dass jeder Commit perfekt sein muss. Gerade beim Lernen oder bei lokaler Experimentierarbeit entstehen unfertige Zwischenstände. Vor einer gemeinsamen Veröffentlichung können diese jedoch oft sinnvoll geordnet, zusammengefasst oder dokumentiert werden.


Praktisches Denkmodell: Fragen an jede Änderung

Bevor du eine Änderung dauerhaft speicherst, helfen diese Fragen:

  1. Was habe ich geändert?
    Beschreibe die technische oder fachliche Wirkung klar.

  2. Warum war die Änderung nötig?
    Gibt es einen Fehler, ein Ticket, eine Anforderung oder eine Entscheidung?

  3. Gehört alles in einen gemeinsamen Schritt?
    Oder lassen sich unabhängige Änderungen trennen?

  4. Ist der neue Zustand funktionsfähig?
    Wurden Tests ausgeführt oder zumindest die Auswirkungen geprüft?

  5. Kann eine andere Person die Änderung später verstehen?
    Reicht die Commit-Nachricht aus, oder braucht es zusätzliche Dokumentation?

  6. Kann die Änderung bei Bedarf isoliert rückgängig gemacht werden?
    Kleine, klar abgegrenzte Commits erleichtern das erheblich.

Diese Fragen machen aus „Dateien speichern“ eine professionelle, nachvollziehbare Arbeitsweise.


Git und die unveränderliche Vergangenheit

Ein zentraler Vorteil von Git ist, dass frühere Commits normalerweise nicht einfach überschrieben werden. Neue Arbeit baut auf bestehenden Zuständen auf.

Wenn du heute einen Commit erstellst, bleibt der vorherige Zustand weiterhin Teil der Historie:

Vorheriger Stand ── Neue Änderung ── Aktueller Stand

Dadurch kannst du:

  • frühere Versionen untersuchen,
  • Änderungen vergleichen,
  • ältere Stände wiederherstellen,
  • Fehler zeitlich eingrenzen,
  • Releases dauerhaft markieren.

Später wirst du lernen, dass Git-Historien unter bestimmten Umständen umgeschrieben werden können, etwa mit git rebase oder speziellen Bereinigungswerkzeugen. Das sind jedoch bewusste, teilweise riskante Operationen — insbesondere bei bereits mit anderen geteilten Commits.

Für den Einstieg gilt:

Neue Arbeit wird als neuer nachvollziehbarer Schritt ergänzt, nicht durch das unkontrollierte Überschreiben alter Arbeit ersetzt.


Zusammenfassung

Änderungen bilden die Grundlage jeder Softwareentwicklung. Versionskontrolle macht aus diesen Änderungen eine zuverlässige Projektgeschichte.

Die wichtigsten Begriffe:

  • Änderung: Eine Anpassung an Dateien oder Projektinhalten.
  • Version: Ein nachvollziehbarer Zustand des gesamten Projekts zu einem bestimmten Zeitpunkt.
  • Commit: Eine in Git gespeicherte, beschriebene Momentaufnahme eines Projektzustands.
  • Historie: Die geordnete Verbindung aller Commits und damit die Entwicklungsgeschichte des Projekts.

Eine gute Projektgeschichte hilft dir dabei,

  • Fehler schneller zu verstehen,
  • ältere Stände sicher wiederzufinden,
  • Änderungen gezielt zurückzunehmen,
  • Releases reproduzierbar zu machen,
  • und Wissen im Team langfristig zu bewahren.

Im nächsten Schritt wird deutlich, wie unterschiedliche Systeme diese Geschichte organisieren: zentral auf einem Server oder verteilt auf die Rechner aller Beteiligten.