Werkzeuge der Softwareentwicklung: Compiler, Debugger, Bibliotheken, Versionsverwaltung, CI/CD und Open Source
https://bildung.social/@oerinformatik/
https://oer-informatik.de/entwicklungswerkzeuge
Softwareentwicklung, Compiler, Interpreter, Debugger, Bytecode, Bibliotheken, Versionsverwaltung, Git, CI/CD, Open Source, Lizenzen
tl/dr; (ca. 30 min Lesezeit): Zwischen dem Quelltext und der laufenden Software steht eine ganze Werkzeugkette. Dieser Artikel erklärt, wie Compiler, Interpreter und virtuelle Maschinen Quelltext ausführbar machen, wie Du Laufzeitfehler mit dem Debugger findest, wann sich Bibliotheken und Frameworks lohnen und wie Du eine eigene Bibliothek dokumentierst, warum ein Team ohne Versionsverwaltung nicht arbeiten sollte und wie CI/CD-Pipelines Software automatisch bauen, testen und ausliefern. Zum Schluss geht es um Open Source: was der Begriff wirklich bedeutet, welche Lizenzen es gibt und warum Du nicht jede Open-Source-Bibliothek ohne Weiteres einsetzen darfst [AI-Assisted]. (Zuletzt geändert am 28.10.2026)
Schon fit in diesem Thema? Nutze die Kann-Liste und die Leitfragen unter dem Artikel als Checkliste, ob Du die relevanten Kompetenzen bereits hast.
Als Softwareentwicklungs-Grundlagen kann ich nach Durchlesen des Artikels…
… den Unterschied zwischen Compiler- und Interpretersprachen beschreiben, je Kategorie Beispielsprachen nennen und Vor- und Nachteile eines Interpreters gegenüber einem Compiler nennen.
… Mischformen wie Bytecode mit virtueller Maschine (Java, C#) erläutern und Kriterien für die Auswahl einer Programmiersprache begründen.
… beschreiben, wozu ein Debugger eingesetzt wird, seine wichtigsten Funktionen (Haltepunkte, Einzelschritte, Variablen beobachten und ändern, Aufrufstapel) nennen und Vorgehensweisen der Fehlersuche mit dem Debugger angeben.
… Vor- und Nachteile beim Einsatz von Bibliotheken und Frameworks beschreiben.
… beschreiben, was die Dokumentation einer selbst entwickelten Bibliothek oder Klasse enthalten muss, und einen Dokumentationskommentar ergänzen.
… die Grundbegriffe der Versionsverwaltung (Repository, Commit, Branch, Merge, Konflikt, Tag) erklären und begründet Stellung nehmen, warum ein Entwicklungsteam eine Versionsverwaltung benötigt.
… beschreiben, wie Software mit einer CI/CD-Pipeline automatisch kompiliert, getestet und bereitgestellt wird, und Vorteile gegenüber der manuellen Bereitstellung nennen.
… den Begriff Open Source erläutern, ihn von Freeware und „einsehbarem Quelltext“ abgrenzen und Vor- und Nachteile von Open-Source-Bibliotheken beschreiben.
… begründen, warum nicht jede Open-Source-Bibliothek ohne Einschränkungen eingesetzt werden darf.
Darüber hinaus kann ich als Softwareentwicklungs-Expertenwissen…
… freizügige Lizenzen und Copyleft-Lizenzen (stark und schwach) unterscheiden und ihre Pflichten für ein konkretes Projekt beurteilen.
… Versionsnummern nach Semantic Versioning vergeben und Abhängigkeiten über einen Paketmanager mit festgelegten Versionen verwalten.
… Continuous Integration, Continuous Delivery und Continuous Deployment voneinander abgrenzen und Maßnahmen gegen Sicherheitslücken in Abhängigkeiten (Abhängigkeitsprüfung, SBOM) erläutern.
Ein Programm besteht nicht nur aus dem Code, den Du selbst schreibst. Bevor eine Webanwendung bei den Nutzenden läuft, wird ihr Quelltext übersetzt, mit fremden Bibliotheken verbunden, in einer Versionsverwaltung mit den Änderungen der anderen im Team zusammengeführt, automatisch getestet und auf einen Server ausgeliefert. All diese Werkzeuge bilden zusammen die Werkzeugkette (toolchain) eines Projekts. Wer sie kennt, kann im Team mitreden, wenn es um die Wahl der Programmiersprache, den Einsatz einer Bibliothek oder die Einführung einer Pipeline geht – und genau solche Entscheidungen begründest Du auch in Aufgaben.
Die Werkzeugkette im Überblick
Wie die Werkzeuge vom ersten Tastendruck bis zur laufenden Software zusammenspielen, zeigt die folgende Übersicht; die Tabelle darunter nennt Aufgaben und Beispiele.
| Werkzeug | Aufgabe | Beispiele |
|---|---|---|
| Editor, integrierte Entwicklungsumgebung (IDE) | Quelltext schreiben, Codevervollständigung, Fehlerhinweise, Debugger, Refactoring, Anbindung von Compiler, Tests und Versionsverwaltung | IntelliJ IDEA, Eclipse, Visual Studio, Visual Studio Code |
| Compiler, Interpreter, Laufzeitumgebung | Quelltext in etwas übersetzen, das ein Prozessor ausführen kann | gcc, javac und JVM, Python-Interpreter |
| Debugger | laufendes Programm anhalten, schrittweise ausführen, Variablen und Aufrufstapel untersuchen | in IDEs integriert (IntelliJ IDEA, Visual Studio Code, Visual Studio), gdb, Entwicklerwerkzeuge im Browser |
| Bibliotheken und Frameworks | fertige, wiederverwendbare Funktionen und Klassen | Java-Klassenbibliothek, Spring Boot, React |
| Paketmanager und Build-Werkzeug | Abhängigkeiten laden, kompilieren, testen, Pakete bauen | Maven, Gradle, npm, pip, NuGet |
| Versionsverwaltung | Änderungen nachvollziehbar speichern, im Team zusammenführen | Git, Subversion |
| CI/CD-Server | bei jeder Änderung automatisch bauen, testen und ausliefern | GitLab CI/CD, GitHub Actions, Jenkins |
Die folgenden Abschnitte gehen diese Kette von vorne nach hinten durch.
Vom Quelltext zum laufenden Programm: Compiler und Interpreter
Ein Prozessor versteht nur Maschinencode: Folgen von Befehlen seines Befehlssatzes in binärer Form. Quelltext in einer Hochsprache wie C, Java oder Python muss deshalb übersetzt werden. Dafür gibt es zwei grundsätzliche Wege – und eine Mischform, die heute sehr verbreitet ist.
Compiler: einmal übersetzen, oft ausführen
Ein Compiler übersetzt den gesamten Quelltext vor der Ausführung in Maschinencode. Er prüft dabei Syntax und Typen, optimiert den Code und erzeugt eine Objektdatei; ein Linker verbindet sie mit den verwendeten Bibliotheken zu einem ausführbaren Programm (z. B. prog.exe). Diese Datei kann danach beliebig oft gestartet werden, ohne dass der Compiler noch gebraucht wird. Wie das bei einem Mikrocontroller im Detail abläuft, zeigt der Artikel Was passiert beim Kompilieren eines Arduino-Programms?.
Typische Compilersprachen: C, C++, Rust, Go, Pascal.
Wichtig: Das erzeugte Programm passt nur zu einem Prozessortyp und Betriebssystem. Ein für Windows auf x86-64 übersetztes Programm läuft nicht auf einem Mikrocontroller oder einem Mac mit ARM-Prozessor – dafür muss neu übersetzt werden.
Interpreter: bei jeder Ausführung neu übersetzen
Ein Interpreter liest den Quelltext zur Laufzeit und führt ihn Anweisung für Anweisung aus. Es entsteht kein dauerhaftes, ausführbares Programm: Bei jeder Ausführung wird erneut übersetzt, und der Interpreter muss auf dem Zielrechner installiert sein.
Typische Interpretersprachen: Python, PHP, JavaScript, Ruby, Bash, PowerShell.
Mischform: Bytecode und virtuelle Maschine
Java und C# gehen einen Mittelweg: Ein Compiler (javac bzw. der C#-Compiler) übersetzt den Quelltext vorab in einen plattformunabhängigen Zwischencode, den Bytecode (bei .NET: Common Intermediate Language, CIL). Diesen führt zur Laufzeit eine virtuelle Maschine aus – die Java Virtual Machine (JVM) bzw. die .NET-Laufzeitumgebung. Sie interpretiert den Bytecode und übersetzt häufig ausgeführte Teile mit einem Just-in-time-Compiler (JIT) in Maschinencode.
Damit vereinen diese Sprachen Vorteile beider Welten: Fehler fallen schon beim Kompilieren auf, derselbe Bytecode läuft auf jedem System mit passender virtueller Maschine („write once, run anywhere“), und durch den JIT ist die Ausführung fast so schnell wie bei kompiliertem Maschinencode. Der Preis: Auf dem Zielsystem muss eine Laufzeitumgebung (z. B. ein Java Runtime Environment) vorhanden sein oder mit dem Programm ausgeliefert werden.
Genau genommen ist „kompiliert“ oder „interpretiert“ eine Eigenschaft der Implementierung, nicht der Sprache: Auch der Standard-Python-Interpreter erzeugt intern Bytecode, JavaScript wird in Browsern per JIT übersetzt, und für C gibt es Interpreter. Meist genügt die übliche Zuordnung: C/C++ als Compilersprachen, Python/PHP als Interpretersprachen, Java/C# als Compilersprachen mit Bytecode.
Compiler und Interpreter im Vergleich
| Merkmal | Compiler | Interpreter |
|---|---|---|
| Zeitpunkt der Übersetzung | einmal vor der Ausführung | bei jeder Ausführung, Anweisung für Anweisung |
| Ergebnis | ausführbare Datei (Maschinencode) | kein dauerhaftes Programm |
| Ausführungsgeschwindigkeit | hoch, Code ist optimiert | geringer, Übersetzung kostet Laufzeit |
| Entwicklungszyklus | Änderung → kompilieren → starten | Änderung → sofort starten, interaktiv testbar |
| Fehlererkennung | Syntax- und Typfehler vor dem Start | viele Fehler erst, wenn die Zeile ausgeführt wird |
| Zielsystem braucht | nur das Programm (und ggf. Bibliotheken) | den Interpreter |
| Plattform | je Prozessor und Betriebssystem neu übersetzen | läuft überall, wo es den Interpreter gibt |
| Weitergabe | Maschinencode, Quelltext bleibt verborgen | Quelltext wird mit ausgeliefert |
Daraus ergeben sich die typischen Vorteile eines Interpreters gegenüber einem Compiler: schneller, einfacher Entwicklungsprozess (ändern und sofort ausprobieren), interaktives Testen, plattformunabhängig. Nachteile: langsamere Ausführung, weil bei jedem Lauf neu übersetzt wird; Fehler zeigen sich erst zur Laufzeit; der Interpreter muss auf jedem Zielsystem installiert sein; der Quelltext ist für alle lesbar.
Welche Programmiersprache passt?
Die Frage „Compiler oder Interpreter?“ ist selten allein entscheidend. Bei der Wahl einer Programmiersprache helfen allgemeine Kriterien, die Du immer mit der konkreten Situation begründen solltest:
Einsatzgebiet und Zielplattform: Mikrocontroller mit wenig Speicher und Akku verlangen effizienten Code (C/C++), Webanwendungen im Browser JavaScript, Datenanalyse profitiert von Python.
Leistung und Ressourcenbedarf: Muss das Programm schnell, energiesparend oder mit wenig Arbeitsspeicher laufen?
Vorhandenes Wissen im Team und Firmenvorgaben: Eine Sprache, die das Team beherrscht, spart Einarbeitung und Fehler.
Verfügbare Bibliotheken, Frameworks und Werkzeuge: Gibt es fertige Lösungen für das Problem und gute Entwicklungsumgebungen?
Zukunftssicherheit: Wird die Sprache weiterentwickelt, gibt es eine aktive Gemeinschaft, findet man auch in fünf Jahren noch Fachkräfte?
Portabilität und Lizenzkosten von Compiler, Laufzeitumgebung oder Bibliotheken.
Fehler finden mit dem Debugger
Der Compiler meldet Syntaxfehler, bevor ein Programm läuft. Viel hartnäckiger sind Laufzeit- und Logikfehler: Das Programm startet, rechnet aber falsch, bricht mit einer Ausnahme ab oder verhält sich nur bei bestimmten Eingaben seltsam. Für diese Fehler gibt es den Debugger.
Wozu ein Debugger dient
Ein Debugger ist ein Werkzeug, mit dem Du ein laufendes Programm kontrolliert anhalten, schrittweise ausführen und seinen Zustand untersuchen kannst. So lässt sich beobachten, was das Programm tatsächlich tut, statt nur zu vermuten, was es tun sollte. Ziel ist, die Ursache eines Fehlers zu finden und den Fehler zu beheben, ohne den Quelltext mit Testausgaben zu überladen. In den meisten IDEs ist ein Debugger eingebaut; das Programm wird dazu im Debug-Modus gestartet.
Die wichtigsten Funktionen
| Funktion | Was sie tut |
|---|---|
| Haltepunkt (Breakpoint) | Das Programm hält an einer markierten Zeile an, bevor sie ausgeführt wird. |
| bedingter Haltepunkt | Das Programm hält nur, wenn eine Bedingung erfüllt ist, z. B. i == 199 oder summe < 0 – praktisch in Schleifen mit vielen Durchläufen. |
| Ausnahme-Haltepunkt | Das Programm hält genau dort, wo eine bestimmte Ausnahme (z. B. NullPointerException) geworfen wird. |
| Einzelschritt: Step Over | führt die aktuelle Zeile aus; ein Methodenaufruf darin wird als Ganzes ausgeführt. |
| Einzelschritt: Step Into | springt in die aufgerufene Methode hinein, um sie Zeile für Zeile zu verfolgen. |
| Step Out | führt die aktuelle Methode zu Ende und hält in der aufrufenden Methode wieder an. |
| Fortsetzen (Resume) | lässt das Programm bis zum nächsten Haltepunkt weiterlaufen. |
| Variablen und Ausdrücke beobachten (Watch) | zeigt die aktuellen Werte von Variablen, Objektattributen und selbst eingegebenen Ausdrücken. |
| Werte zur Laufzeit ändern | setzt eine Variable auf einen anderen Wert, um zu testen, wie das Programm darauf reagiert. |
| Aufrufstapel (Call Stack) | zeigt, über welche Methodenaufrufe das Programm an die aktuelle Stelle gekommen ist. |
| Logpoint | schreibt an einer Stelle eine Meldung ins Protokoll, ohne anzuhalten und ohne den Quelltext zu ändern. |
Vorgehen bei der Fehlersuche
Ein Beispiel: Eine Funktion soll die Summe eines Warenkorbs berechnen. Für die Preise 19,90 €, 5,00 € und 12,50 € erwartest Du 37,40 €, das Programm liefert aber 24,90 €.
def warenkorb_summe(preise):
summe = 0
for i in range(len(preise) - 1):
summe = summe + preise[i]
return summe
print(warenkorb_summe([19.90, 5.00, 12.50])) # erwartet 37.4, Ausgabe 24.9So gehst Du mit dem Debugger vor:
- Fehler reproduzieren: Eine Eingabe festhalten, bei der der Fehler sicher auftritt (hier die drei Preise), und das erwartete Ergebnis notieren.
- Haltepunkt vor die verdächtige Stelle setzen: z. B. auf die Zeile
summe = summe + preise[i]und das Programm im Debug-Modus starten. - Schrittweise ausführen und Variablen beobachten: Mit Step Over Durchlauf für Durchlauf weitergehen und
i,preise[i]undsummeim Variablenfenster verfolgen. Hier sieht man:inimmt nur die Werte 0 und 1 an, der letzte Preis wird nie addiert. - Fehlerstelle eingrenzen: Stimmen die Werte noch, den Haltepunkt weiter nach hinten setzen; sind sie schon falsch, weiter nach vorne. Bei langen Abläufen halbiert man so den verdächtigen Bereich (bedingte Haltepunkte helfen in Schleifen).
- Ursache beheben und erneut prüfen:
range(len(preise))stattrange(len(preise) - 1), danach denselben Fall noch einmal ausführen. Am besten wird der Fall als Unittest festgehalten, damit der Fehler nicht zurückkommt.
Weitere Wege, die Aufgaben und Praxis kennen:
- Werte im Debugger ändern, um eine Vermutung zu prüfen (Was passiert, wenn
summehier schon 37,40 ist?). - Aufrufstapel ansehen, wenn eine Methode mit falschen Parametern aufgerufen wird: Von wo kam der Aufruf?
- Kontrollausgaben oder Logging an bestimmten Stellen einbauen. Das geht auch ohne Debugger und ist auf Servern, in Containern oder auf Mikrocontrollern oft der einzige Weg; die Ausgaben müssen danach aber wieder entfernt oder auf eine passende Protokollstufe gesetzt werden.
- Remote-Debugging: Der Debugger der IDE verbindet sich mit einem Programm, das auf einem anderen Rechner, in einem Container oder auf einem eingebetteten Gerät läuft (z. B. über eine Debug-Schnittstelle wie JTAG oder SWD).
Debugger und Tests ergänzen sich: Tests zeigen, dass ein Fehler vorliegt, der Debugger zeigt, wo und warum.
Bibliotheken und Frameworks: das Rad nicht neu erfinden
Eine Bibliothek (library) ist eine Sammlung fertiger, wiederverwendbarer Funktionen oder Klassen, die über eine dokumentierte Schnittstelle (API, application programming interface) genutzt werden. Bei objektorientierten Sprachen spricht man von einer Klassenbibliothek – etwa der Java-Klassenbibliothek mit java.util.ArrayList, java.time.LocalDate oder java.net.http.HttpClient. Zu jeder Sprache gehört eine Standardbibliothek; dazu kommen unzählige Bibliotheken von Drittanbietern, z. B. für PDF-Erzeugung, JSON-Verarbeitung oder Verschlüsselung.
Ein Framework geht weiter: Es gibt das Gerüst einer ganzen Anwendung vor und ruft Deinen Code an festgelegten Stellen auf. Bei einer Bibliothek rufst Du die Bibliothek auf, bei einem Framework ruft das Framework Dich auf (Inversion of Control, „Don’t call us, we’ll call you“). Beispiele sind Spring Boot für Java-Backends, ASP.NET Core oder Angular.
Bibliotheken einbinden und Versionen verwalten
Bibliotheken werden heute kaum noch von Hand heruntergeladen. Ein Paketmanager lädt sie aus einem zentralen Verzeichnis (z. B. Maven Central, npm-Registry, PyPI) – samt allen Bibliotheken, die sie selbst benötigen (transitive Abhängigkeiten). Welche Bibliotheken in welcher Version gebraucht werden, steht in einer Konfigurationsdatei im Projekt, bei Maven z. B. in der pom.xml (mehr dazu im Artikel Build-Tool Maven):
<dependency>
<groupId>org.mariadb.jdbc</groupId>
<artifactId>mariadb-java-client</artifactId>
<version>3.4.1</version>
</dependency>Viele Projekte vergeben Versionsnummern nach Semantic Versioning (MAJOR.MINOR.PATCH):
PATCH (3.4.1 → 3.4.2): Fehlerbehebung, die Schnittstelle bleibt gleich.
MINOR (3.4.2 → 3.5.0): neue Funktionen, aber abwärtskompatibel – bestehender Code läuft weiter.
MAJOR (3.5.0 → 4.0.0): inkompatible Änderung (breaking change), z. B. geänderte oder entfernte Methoden; nutzende Programme müssen angepasst werden.
Damit alle im Team und die Pipeline exakt dieselben Versionen verwenden, werden Versionen fest eingetragen oder in einer Lock-Datei (z. B. package-lock.json) festgehalten.
Vor- und Nachteile von Bibliotheken
| Vorteile | Nachteile |
|---|---|
| Vorhandene Funktionen müssen nicht neu entwickelt werden: spart Zeit und Kosten. | Einarbeitungsaufwand in Schnittstelle und Dokumentation. |
| Häufig genutzte Bibliotheken sind ausgiebig getestet und oft fehlerärmer als eine Eigenentwicklung. | Overhead: Die Bibliothek bringt mehr mit als gebraucht wird (Programmgröße, Speicher, ggf. Laufzeit). |
| Fehlerbehebungen und Verbesserungen kommen durch ein Update – bei gleicher Schnittstelle ohne Änderung an der eigenen Anwendung. | Abhängigkeit: Wird die Bibliothek nicht mehr gepflegt oder ändert sie ihre Schnittstelle, muss das eigene Projekt reagieren. |
| Einheitliche, bekannte Lösungen erleichtern neuen Teammitgliedern den Einstieg. | Sicherheitslücken in der Bibliothek werden zu Sicherheitslücken der eigenen Anwendung. |
| Fachwissen anderer (z. B. Kryptografie, Datumsrechnung) wird nutzbar. | Lizenzbedingungen müssen eingehalten werden; Versionskonflikte zwischen Abhängigkeiten sind möglich. |
Wie gefährlich eine einzige Abhängigkeit werden kann, zeigte im Dezember 2021 die Sicherheitslücke Log4Shell (CVE-2021-44228) in der weit verbreiteten Java-Protokollierungsbibliothek Log4j: Über eine präparierte Zeichenkette in einer Logmeldung ließ sich Code aus der Ferne ausführen. Das BSI rief die Warnstufe Rot aus, und viele Unternehmen mussten erst einmal herausfinden, in welchen ihrer Programme Log4j überhaupt steckte.
Eine eigene Bibliothek erweitern und dokumentieren
Erweiterst Du eine Bibliothek um eigene Klassen, die andere nutzen sollen, ist die Dokumentation der Schnittstelle Teil der Arbeit. Die Nutzenden sehen Deinen Quelltext oft nicht – sie verlassen sich auf die Beschreibung. Eine gute Dokumentation einer Klasse enthält:
eine zusammenfassende Beschreibung der Klasse: Zweck, Verantwortung, Einordnung (Paket/Namespace), ggf. Beispiel für die typische Verwendung;
Oberklasse und implementierte Interfaces sowie geerbte Methoden, die mitgenutzt werden können;
alle öffentlichen Konstruktoren und Methoden, je Methode: Wirkung (was tut sie, welche Seiteneffekte hat sie?), jeden Parameter mit Datentyp, Bedeutung und gültigem Wertebereich, den Rückgabewert mit Datentyp und Bedeutung, mögliche Ausnahmen (Exceptions) und wann sie auftreten;
Voraussetzungen für die Anwendung (Vorbedingungen, benötigte Konfiguration, Thread-Sicherheit, Abhängigkeiten) und Nachbedingungen;
Versionsangaben: seit welcher Version es eine Methode gibt (
@since), veraltete Methoden (@deprecatedmit Ersatz), Änderungsprotokoll (Changelog) und Lizenz.
In Java schreibst Du diese Angaben als Javadoc-Kommentar direkt über die Klasse bzw. Methode; das Werkzeug javadoc erzeugt daraus HTML-Seiten. C# nutzt XML-Kommentare (/// <summary>), Python Docstrings.
/**
* Fragt den Lagerbestand eines Artikels beim Großhändler ab.
*
* @param artikelnummer Artikelnummer des Großhändlers, nicht null, z. B. "BRE-HYD-01"
* @param lager Nummer des Lagers (1 bis 3)
* @return verfügbare Stückzahl (mindestens 0)
* @throws IllegalArgumentException wenn das Lager nicht zwischen 1 und 3 liegt
* @throws GrosshandelException wenn der Großhändler nicht erreichbar ist
* @since 1.2
*/
public int bestandAbfragen(String artikelnummer, int lager) throws GrosshandelException { … }Versionsverwaltung: Wer hat was wann und warum geändert?
Ohne Versionsverwaltung sehen Projektordner oft so aus: sessionplan_final.zip, sessionplan_final_v2.zip, sessionplan_final_v2_NEU_mia.zip. Spätestens wenn zwei Personen gleichzeitig dieselbe Datei ändern, überschreibt die eine die Arbeit der anderen – und niemand weiß mehr, welcher Stand der richtige ist.
Eine Versionsverwaltung (version control system, VCS) speichert alle Änderungen an den Dateien eines Projekts nachvollziehbar und führt die Arbeit mehrerer Personen zusammen. Heute ist fast immer Git im Einsatz.
Grundbegriffe
Repository (Repo): die Projektablage mit allen Dateien und ihrer vollständigen Geschichte.
Commit: ein gespeicherter Schnappschuss des Projekts mit Nachricht („Warteliste: Nachrücken bei Abmeldung“), Autor*in, Zeitpunkt und Verweis auf den Vorgänger. Bei Git ist jeder Commit über eine Prüfsumme (Hash) eindeutig benannt.
Diff: die Unterschiede zwischen zwei Ständen, Zeile für Zeile.
Branch (Zweig): eine eigene Entwicklungslinie, z. B. für eine neue Funktion, ohne den lauffähigen Hauptzweig (
main) zu stören.Merge: das Zusammenführen eines Branches in einen anderen. Haben zwei Personen dieselbe Stelle unterschiedlich geändert, entsteht ein Merge-Konflikt: Die Versionsverwaltung überschreibt nichts, sondern markiert die Stelle, und ein Mensch entscheidet.
Tag: eine feste Markierung eines Commits, meist für eine ausgelieferte Version (
v1.4.0).Clone, Push, Pull: Repository kopieren, eigene Commits zum gemeinsamen Server hochladen, Änderungen der anderen herunterladen.
Merge Request bzw. Pull Request: Antrag, einen Branch in den Hauptzweig zu übernehmen – der übliche Ort für Code-Reviews und automatische Tests.
Zentral oder verteilt
Bei einer zentralen Versionsverwaltung (z. B. Subversion) liegt die Geschichte nur auf einem Server; ohne Verbindung kann niemand committen. Bei einer verteilten Versionsverwaltung (z. B. Git) hat jede Kopie die vollständige Geschichte: Du kannst offline committen, Branches lokal anlegen und erst später alles auf einen gemeinsamen Server (GitLab, GitHub, Gitea …) übertragen. Jede Kopie ist zugleich eine Sicherung.
Ein typischer Arbeitsablauf mit Git
git clone https://gitlab.example/barcamp/sessionplan.git # Repository holen
git switch -c feature/warteliste # eigenen Branch anlegen
# ... Dateien bearbeiten ...
git status # was hat sich geändert?
git add Warteliste.java # Änderung vormerken
git commit -m "Warteliste: Nachrücken bei Abmeldung" # lokal speichern
git push -u origin feature/warteliste # auf den Server laden
# danach im Webportal einen Merge Request stellen, Review abwarten, mergenZwei Regeln gehören von Anfang an dazu: Zugangsdaten, Passwörter und Schlüssel gehören nie ins Repository (sie bleiben sonst in der Geschichte für immer sichtbar), und erzeugte Dateien wie kompilierte Klassen oder heruntergeladene Bibliotheken werden über die Datei .gitignore ausgeschlossen. Wie Du ein erstes Repository anlegst und lokal committest, zeigt der Artikel Git: Repository anlegen und lokal committen.
Warum jedes Team eine Versionsverwaltung braucht
Soll ein Team mit zehn Personen eine Versionsverwaltung einsetzen? Die Antwort ist eindeutig ja – und so lässt sie sich begründen:
Paralleles Arbeiten: Bei mehreren Personen reichen Absprachen nicht aus. Ohne Versionsverwaltung überschreiben sich gleichzeitige Änderungen unbemerkt; mit ihr werden Änderungen zusammengeführt und Konflikte sichtbar gemacht.
Nachvollziehbarkeit: Zu jeder Zeile ist bekannt, wer sie wann und warum geändert hat – wichtig für Fehlersuche, Reviews und Haftungsfragen.
Wiederherstellbarkeit: Jeder frühere Stand kann zurückgeholt werden; eine fehlerhafte Änderung lässt sich gezielt rückgängig machen (
git revert).Isolierte Entwicklung: Neue Funktionen entstehen in Branches, der Hauptzweig bleibt lauffähig.
Releases: Ausgelieferte Versionen werden markiert; ein Fehler in Version 1.4.0 lässt sich am genau passenden Stand untersuchen.
Grundlage für Automatisierung: Code-Reviews per Merge Request und CI/CD-Pipelines setzen auf der Versionsverwaltung auf.
Übrigens lohnt sich eine Versionsverwaltung auch, wenn Du allein programmierst – schon wegen Historie und Wiederherstellbarkeit.
CI/CD: automatisch bauen, testen und ausliefern
Stell Dir vor, jemand aus dem Team kompiliert die Software auf dem eigenen Laptop, kopiert das Ergebnis per Hand auf den Server und startet den Dienst neu. Das funktioniert – bis jemand einen Schritt vergisst, eine Konfigurationsdatei fehlt, auf dem Laptop eine andere Compilerversion installiert ist („works on my machine“) oder ungetesteter Code live geht. Genau solche Probleme löst CI/CD.
Continuous Integration, Delivery und Deployment
Continuous Integration (CI): Alle im Team führen ihre Änderungen häufig (mindestens täglich) im gemeinsamen Repository zusammen. Jeder Push bzw. Merge Request löst automatisch einen Build aus: Abhängigkeiten laden, kompilieren, Unit-Tests und statische Codeanalyse ausführen. Schlägt ein Schritt fehl, bekommt das Team sofort eine Rückmeldung, und die Änderung wird nicht übernommen.
Continuous Delivery (CD): Nach erfolgreicher Integration wird die Software automatisch zu einem auslieferbaren Paket gebaut und in eine Testumgebung (Staging) übertragen. Jeder Stand könnte jederzeit live gehen; die Auslieferung in die Produktion erfolgt aber erst nach einer manuellen Freigabe (ein Klick in der Pipeline).
Continuous Deployment: Auch die Auslieferung in die Produktion läuft automatisch, sobald alle Prüfungen bestanden sind – ohne menschliche Freigabe. Das verlangt eine sehr gute Testabdeckung.
Die Schritte werden in einer Pipeline beschrieben, die ein CI/CD-Server (z. B. GitLab CI/CD, GitHub Actions, Jenkins) in einer frischen, immer gleichen Umgebung ausführt – meist in einem Container.
Wie eine Pipeline umgesetzt wird
Die Pipeline wird als Datei im Repository beschrieben – sie ist damit selbst versioniert. Ausgelöst wird sie durch Ereignisse: einen Push, einen Merge Request, das Setzen eines Tags (z. B. für ein Release) oder einen Zeitplan (nächtlicher Build). Eine vereinfachte GitLab-Pipeline für ein Java-Projekt:
stages: [build, test, package, deploy]
build:
stage: build
image: maven:3.9-eclipse-temurin-21 # immer gleiche Build-Umgebung
script: mvn -B compile
test:
stage: test
image: maven:3.9-eclipse-temurin-21
script: mvn -B verify # Unit-Tests, Codeanalyse
package:
stage: package
image: maven:3.9-eclipse-temurin-21
script: mvn -B package -DskipTests
artifacts:
paths: [target/*.jar] # Ergebnis aufbewahren
deploy_staging:
stage: deploy
script: ./deploy.sh staging # Zugangsdaten als geschützte CI-Variable
rules:
- if: $CI_COMMIT_BRANCH == "main" # nur Stände aus dem Hauptzweig
deploy_produktion:
stage: deploy
script: ./deploy.sh produktion
rules:
- if: $CI_COMMIT_TAG # nur markierte Versionen (Tags)
when: manual # Continuous Delivery: Freigabe per KlickVorteile gegenüber der manuellen Bereitstellung
Automatische Tests in einer gleichbleibenden Umgebung: Jede Änderung wird vor der Auslieferung gebaut und getestet; Fehler fallen früh auf, nicht erst bei den Nutzenden.
Einheitliche, reproduzierbare Bereitstellung: Immer dieselben Schritte in derselben Reihenfolge, auf Test- und Produktivsystem gleich – keine vergessenen Schritte, keine abweichenden Compilerversionen.
Weniger Zugänge zu Servern: Zugangsdaten liegen geschützt in der Pipeline; nicht jede*r Entwickler*in braucht direkten Zugriff auf Staging- und Produktivsysteme.
Schnelles Rollback: Geht doch etwas schief, wird die Pipeline des letzten funktionierenden Stands (z. B. des vorigen Tags) erneut ausgeführt.
Häufigere, kleinere Auslieferungen: kleinere Änderungen sind risikoärmer und schneller bei den Kund*innen.
Nachvollziehbarkeit: Wer wann was ausgeliefert hat, steht im Protokoll der Pipeline.
Dem stehen Aufwand für Einrichtung und Pflege der Pipeline gegenüber; außerdem ist eine Pipeline nur so gut wie die Tests, die sie ausführt. Und weil sie Zugangsdaten zu den Servern kennt, muss sie selbst gut geschützt werden.
Sicherheitslücken in Abhängigkeiten früh erkennen
Pipelines helfen auch gegen Probleme wie Log4Shell: Ein Schritt zur Abhängigkeitsprüfung (dependency scanning) vergleicht bei jedem Build alle verwendeten Bibliotheken und Versionen mit Datenbanken bekannter Schwachstellen (CVE) und bricht bei kritischen Funden ab. Eine automatisch erzeugte Software-Stückliste (Software Bill of Materials, SBOM) listet alle enthaltenen Komponenten auf, sodass bei einer neuen Warnung sofort klar ist, welche Produkte betroffen sind. Ist ein Update da, bringt die Pipeline es in kurzer Zeit getestet in Produktion. Der Cyber Resilience Act der EU (Verordnung (EU) 2024/2847) verpflichtet Hersteller von Produkten mit digitalen Elementen ab Dezember 2027 ausdrücklich dazu, die enthaltenen Komponenten zu kennen (u. a. mit einer SBOM) und Schwachstellen zu behandeln; aktiv ausgenutzte Schwachstellen müssen sie bereits seit September 2026 melden.
Open Source: mehr als „der Quelltext ist sichtbar“
Was Open Source bedeutet
Bei Open-Source-Software ist der Quelltext öffentlich zugänglich und eine Lizenz erlaubt allen, die Software zu nutzen, zu untersuchen, zu verändern und – auch verändert – weiterzugeben. Welche Lizenzen als Open Source gelten, legt die Open Source Initiative (OSI) in der Open Source Definition mit zehn Kriterien fest, u. a.:
freie Weitergabe, auch gegen Geld, ohne Lizenzgebühren an die Urheber*innen,
der Quelltext muss verfügbar sein,
Veränderungen und abgeleitete Werke müssen erlaubt sein,
keine Benachteiligung von Personen, Gruppen oder Einsatzbereichen (eine Lizenz „nur für nichtkommerzielle Zwecke“ ist also nicht Open Source).
Sehr ähnlich, aber mit Betonung der Freiheit der Nutzenden, definiert die Free Software Foundation freie Software über vier Freiheiten: Programm ausführen, untersuchen und anpassen, weitergeben, verbessern und Verbesserungen veröffentlichen. „Frei“ meint dabei Freiheit, nicht kostenlos.
Abgrenzung zu ähnlichen Begriffen:
| Begriff | Quelltext verfügbar? | Ändern und weitergeben erlaubt? | Beispiel |
|---|---|---|---|
| Open Source / freie Software | ja | ja, nach Lizenzbedingungen | Linux-Kernel, Git, LibreOffice |
| Quelltext nur einsehbar (source available) | ja | nein oder stark eingeschränkt | Code auf einer Plattform ohne Lizenzdatei, Lizenzen mit Nutzungsverboten |
| Freeware | nein | nein, nur kostenlos nutzen | kostenlose Tools mit geschlossenem Quelltext |
| proprietäre Software | nein (selten auf Anfrage) | nein, Nutzung nach Lizenzvertrag | Microsoft Office, viele Branchenlösungen |
Merke: „Der Quelltext ist öffentlich einsehbar“ beschreibt Open Source nur zur Hälfte. Code, der ohne Lizenz auf einer öffentlichen Plattform liegt, darfst Du nach Urheberrecht zwar lesen, aber nicht in Dein Projekt übernehmen.
Open-Source-Lizenzen: freizügig oder Copyleft
| Lizenztyp | Kernregel | Beispiele |
|---|---|---|
| freizügig (permissive) | Nutzen, ändern, auch in geschlossener Software weitergeben – Lizenztext und Urheberhinweis müssen erhalten bleiben. | MIT, BSD, Apache 2.0 (mit Patentklausel) |
| schwaches Copyleft | Änderungen an der Bibliothek selbst müssen unter derselben Lizenz offengelegt werden; das Programm, das sie nur nutzt, darf proprietär bleiben (bei der LGPL muss die Bibliothek austauschbar sein). | LGPL, MPL 2.0, EPL 2.0 |
| starkes Copyleft | Wer ein abgeleitetes Werk weitergibt, muss es vollständig unter derselben Lizenz mit Quelltext weitergeben. | GPL-2.0, GPL-3.0 |
| Netzwerk-Copyleft | wie GPL, gilt zusätzlich, wenn die Software nur über ein Netzwerk genutzt wird (Webanwendung, SaaS). | AGPL-3.0 |
Lizenzen werden in Projekten oft mit einer kurzen SPDX-Kennung angegeben, z. B. MIT, Apache-2.0 oder GPL-3.0-only.
Darf ich jede Open-Source-Bibliothek ohne Einschränkungen einsetzen?
Nein. Open-Source-Software steht unter Lizenzen, und deren Bedingungen müssen eingehalten werden. Das bedeutet nicht, dass der Einsatz verboten ist – aber er ist an Pflichten geknüpft:
Bei Copyleft-Lizenzen kann der Einsatz dazu führen, dass Du Dein eigenes Programm bei der Weitergabe ebenfalls offenlegen musst. Für eine Software, die als geschlossenes Produkt verkauft werden soll, scheidet eine GPL-Bibliothek damit meist aus; bei einer AGPL-Bibliothek reicht schon der Betrieb als Webanwendung.
Auch freizügige Lizenzen verlangen Hinweispflichten (Lizenztext und Copyright mitliefern, z. B. in einer „Third-Party-Notices“-Datei).
Lizenzen müssen zueinander passen: Mehrere Bibliotheken mit unverträglichen Lizenzen (z. B. GPL-2.0-only und Apache 2.0) lassen sich nicht in einem weitergegebenen Programm kombinieren.
Unternehmen haben oft eigene Richtlinien, welche Lizenzen erlaubt sind; dazu kommen Fragen nach Pflege und Sicherheit der Bibliothek.
Vor- und Nachteile von Open-Source-Bibliotheken
| Vorteile | Nachteile |
|---|---|
| Transparenz: Jede*r kann nachvollziehen, was die Bibliothek tut; keine versteckten Funktionen, eigene Prüfung (Audit) möglich. | Kein vertraglicher Anspruch auf Support, Gewährleistung oder Fehlerbehebung (außer über kommerzielle Anbieter). |
| In der Regel keine Lizenzkosten. | Die Pflege hängt oft an wenigen, teils ehrenamtlichen Personen; Projekte können eingestellt werden. |
| Große Gemeinschaft: Fehler werden oft schnell gefunden und behoben; viele Beispiele und Antworten im Netz. | Lizenzpflichten (Copyleft, Hinweispflichten) müssen geprüft und eingehalten werden. |
| Anpassbar: Fehler kann man notfalls selbst beheben, Funktionen ergänzen. | Auch Angreifende können den Quelltext lesen und gezielt nach Schwachstellen suchen. |
| Keine Bindung an einen Hersteller (Vendor-Lock-in), offene Standards. | Lieferkettenangriffe: Manipulierte Pakete oder eingeschleuste Hintertüren (wie 2024 in der Kompressionsbibliothek xz Utils) gelangen über Updates in viele Projekte. |
Zum Punkt „Angreifende können den Code lesen“: Das stimmt, macht Open Source aber nicht grundsätzlich unsicherer. Sicherheit durch Geheimhaltung (security through obscurity) ist kein verlässlicher Schutz – auch geschlossener Code wird analysiert. Entscheidend ist, wie aktiv ein Projekt gepflegt wird und wie schnell Lücken geschlossen werden. Bei weit verbreiteten Projekten schauen viele Augen auf den Code, bei kleinen, kaum gepflegten Bibliotheken dagegen oft niemand.
Proprietäre Software hat umgekehrt ihre eigenen Stärken: vertraglicher Support und Gewährleistung, ein verantwortlicher Hersteller, oft bessere Zusammenarbeit mit weiteren Produkten desselben Herstellers – dafür Lizenzkosten und Abhängigkeit vom Hersteller.
Fazit
Compiler, Interpreter und virtuelle Maschinen machen Quelltext ausführbar; ihre Eigenschaften beeinflussen Geschwindigkeit, Entwicklungstempo und Portabilität. Bibliotheken sparen Arbeit, bringen aber Abhängigkeiten, Lizenzpflichten und mögliche Sicherheitslücken mit – und eigene Erweiterungen brauchen eine Dokumentation, mit der andere arbeiten können. Eine Versionsverwaltung ist für jedes Team Pflicht, und CI/CD-Pipelines machen aus vielen fehleranfälligen Handgriffen einen reproduzierbaren, getesteten Ablauf. Open Source schließlich heißt nicht nur „Code ansehen“, sondern „unter Lizenzbedingungen nutzen, ändern und weitergeben“.
Zum Üben gibt es Übungsaufgaben zu Werkzeugen der Softwareentwicklung.
Leitfragen
- Worin unterscheiden sich Compiler- und Interpretersprachen, welche Vor- und Nachteile hat ein Interpreter, und wie ordnest Du Java und C# ein?
Ein Compiler übersetzt den gesamten Quelltext vorab in Maschinencode, ein Interpreter übersetzt ihn bei jeder Ausführung Anweisung für Anweisung zur Laufzeit. Vorteile des Interpreters sind ein schneller Entwicklungszyklus, interaktives Testen und Plattformunabhängigkeit, Nachteile die langsamere Ausführung, Fehler erst zur Laufzeit, ein nötiger Interpreter auf dem Zielsystem und lesbarer Quelltext. Java und C# sind eine Mischform: Ein Compiler erzeugt Bytecode, den eine virtuelle Maschine (JVM bzw. .NET) mit JIT-Compiler ausführt, siehe Abschnitt „Compiler und Interpreter im Vergleich“ und Abschnitt „Mischform: Bytecode und virtuelle Maschine“.
- Welche Kriterien sind bei der Auswahl einer Programmiersprache wichtig?
Wichtig sind Einsatzgebiet und Zielplattform, Leistung und Ressourcenbedarf, vorhandenes Wissen im Team und Firmenvorgaben, verfügbare Bibliotheken, Frameworks und Werkzeuge, Zukunftssicherheit sowie Portabilität und Lizenzkosten. Begründe Deine Wahl immer mit der konkreten Situation, siehe Abschnitt „Welche Programmiersprache passt?“.
- Wozu dient ein Debugger, welche Funktionen bietet er, und wie gehst Du bei der Fehlersuche damit vor?
Mit einem Debugger hältst Du ein laufendes Programm an, führst es schrittweise aus und untersuchst seinen Zustand, um die Ursache von Laufzeit- und Logikfehlern zu finden. Funktionen: (bedingte) Haltepunkte, Step Over/Into/Out, Fortsetzen, Variablen beobachten und ändern, Aufrufstapel. Vorgehen: Fehler reproduzieren, Haltepunkt vor die verdächtige Stelle setzen, schrittweise ausführen und Variablen beobachten, Fehlerstelle eingrenzen, beheben und mit einem Test absichern, siehe Abschnitt „Fehler finden mit dem Debugger“.
- Welche Vor- und Nachteile hat der Einsatz einer Bibliothek, und was muss die Dokumentation einer selbst geschriebenen Bibliotheksklasse enthalten?
Bibliotheken sparen Zeit und Kosten, sind oft gut getestet, liefern Fehlerbehebungen per Update und machen Fachwissen anderer nutzbar; dafür kosten sie Einarbeitung, bringen Overhead, Abhängigkeiten, mögliche Sicherheitslücken und Lizenzpflichten mit. Die Dokumentation einer eigenen Klasse enthält eine Beschreibung der Klasse, Oberklasse und Interfaces, alle öffentlichen Konstruktoren und Methoden mit Wirkung, Parametern, Rückgabewert und Exceptions, Vor- und Nachbedingungen sowie Versionsangaben wie @since oder @deprecated, siehe Abschnitt „Vor- und Nachteile von Bibliotheken“ und Abschnitt „Eine eigene Bibliothek erweitern und dokumentieren“.
- Was bedeuten Repository, Commit, Branch, Merge, Merge-Konflikt und Tag, und warum braucht ein Entwicklungsteam eine Versionsverwaltung?
Ein Repository ist die Projektablage mit vollständiger Geschichte, ein Commit ein gespeicherter Schnappschuss mit Nachricht, ein Branch eine eigene Entwicklungslinie, ein Merge das Zusammenführen von Branches; bei einem Merge-Konflikt wurde dieselbe Stelle unterschiedlich geändert, und ein Mensch entscheidet; ein Tag markiert einen Commit, meist eine ausgelieferte Version. Ein Team braucht eine Versionsverwaltung für paralleles Arbeiten ohne gegenseitiges Überschreiben, Nachvollziehbarkeit, Wiederherstellbarkeit, isolierte Entwicklung in Branches, markierte Releases und als Grundlage für Reviews und CI/CD, siehe Abschnitt „Grundbegriffe“ und Abschnitt „Warum jedes Team eine Versionsverwaltung braucht“.
- Wie kann die Kompilierung und Bereitstellung einer Software automatisiert werden, was unterscheidet dabei Continuous Integration, Continuous Delivery und Continuous Deployment, und welche Vorteile hat die Automatisierung gegenüber der manuellen Bereitstellung?
Mit einer CI/CD-Pipeline, die als Datei im Repository beschrieben ist und bei Ereignissen wie Push, Merge Request oder Tag in einer frischen, gleichbleibenden Umgebung automatisch baut, testet, paketiert und ausliefert. Bei Continuous Integration werden Änderungen häufig zusammengeführt und jeder Push löst automatisch Build, Tests und Codeanalyse aus. Continuous Delivery baut danach automatisch ein auslieferbares Paket und bringt es in eine Testumgebung, die Produktion folgt nach manueller Freigabe; bei Continuous Deployment geht auch die Auslieferung in die Produktion ohne menschliche Freigabe automatisch. Vorteile gegenüber der manuellen Bereitstellung sind automatische Tests vor jeder Auslieferung, eine einheitliche, reproduzierbare Bereitstellung, weniger direkte Serverzugänge, schnelles Rollback, häufigere kleinere Auslieferungen und Nachvollziehbarkeit, siehe Abschnitt „Continuous Integration, Delivery und Deployment“, Abschnitt „Wie eine Pipeline umgesetzt wird“ und Abschnitt „Vorteile gegenüber der manuellen Bereitstellung“.
- Was bedeutet Open Source, und wie unterscheidet sich Open Source von Freeware?
Open Source heißt: Der Quelltext ist öffentlich zugänglich und eine Lizenz erlaubt allen, die Software zu nutzen, zu untersuchen, zu verändern und weiterzugeben (Kriterien der Open Source Definition der OSI). Freeware darfst Du dagegen nur kostenlos nutzen – der Quelltext ist nicht verfügbar, Ändern und Weitergeben sind nicht erlaubt, siehe Abschnitt „Was Open Source bedeutet“.
- Warum darfst Du nicht jede Open-Source-Bibliothek ohne Einschränkungen einsetzen, und welche Vor- und Nachteile haben Open-Source-Bibliotheken?
Open-Source-Bibliotheken stehen unter Lizenzen, deren Pflichten Du einhalten musst: Copyleft-Lizenzen wie GPL oder AGPL können verlangen, dass Du Dein eigenes Programm offenlegst, auch freizügige Lizenzen haben Hinweispflichten, Lizenzen müssen zueinander passen, und Unternehmen haben eigene Richtlinien. Vorteile sind Transparenz, meist keine Lizenzkosten, eine große Gemeinschaft, Anpassbarkeit und keine Herstellerbindung; Nachteile sind fehlender vertraglicher Support, unsichere Pflege, Lizenzpflichten und Lieferkettenangriffe, siehe Abschnitt „Darf ich jede Open-Source-Bibliothek ohne Einschränkungen einsetzen?“ und Abschnitt „Vor- und Nachteile von Open-Source-Bibliotheken“.
Links und weitere Informationen
als Nachschlagewerk: The Open Source Definition der Open Source Initiative
als Nachschlagewerk: Semantic Versioning 2.0.0 (deutsch)
zum Vertiefen: Pro Git – das Git-Buch (deutsch)
zum Vertiefen: SPDX-Lizenzliste mit allen Lizenzkennungen
Hinweis zur Nachnutzung als Open Educational Resource (OER)
Dieser Artikel und seine Texte, Bilder, Grafiken, Code und sonstiger Inhalt sind - sofern nicht anders angegeben - lizenziert unter CC BY 4.0. Nennung gemäß TULLU-Regel bitte wie folgt: “Werkzeuge der Softwareentwicklung: Compiler, Debugger, Bibliotheken, Versionsverwaltung, CI/CD und Open Source” von oer-informatik.de (H. Stein), Lizenz: CC BY 4.0. Der Artikel wurde unter https://oer-informatik.de/entwicklungswerkzeuge veröffentlicht, die Quelltexte sind in weiterverarbeitbarer Form verfügbar im Repository unter https://gitlab.com/oer-informatik/programmierung/programmiergrundlagen. Erstellt mit KI-Assistenz; von mir umfassend redaktionell überarbeitet, geprüft und freigegeben. Stand: 28.10.2026.
[Kommentare zum Artikel lesen, schreiben] / [Artikel teilen] / [gitlab-Issue zum Artikel schreiben]
