<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Software Engineering &amp; Programming Archives - Creatronix</title>
	<atom:link href="https://creatronix.de/category/software-engineering/feed/" rel="self" type="application/rss+xml" />
	<link>https://creatronix.de/category/software-engineering/</link>
	<description>My adventures in code &#38; business</description>
	<lastBuildDate>Sun, 29 Mar 2026 06:21:46 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.9.4</generator>
	<item>
		<title>Das große Git-Tutorial</title>
		<link>https://creatronix.de/das-grose-git-tutorial/</link>
		
		<dc:creator><![CDATA[Jörn]]></dc:creator>
		<pubDate>Thu, 26 Mar 2026 20:10:53 +0000</pubDate>
				<category><![CDATA[Software Engineering & Programming]]></category>
		<guid isPermaLink="false">https://creatronix.de/?p=7965</guid>

					<description><![CDATA[<p>Motivation Jeder kennt und nutzt Git. Es gibt bis auf Ausnahmen (ich schaue dich an, Perforce) keine ernstzunehmenden Alternativen mehr. Ich lasse Azubis ab der ersten Woche schon mit Git arbeiten, allerdings zunächst im Kontext einer IDE, damit sie ihren Code entspannt versionieren können. Im zweiten Lehrjahr gibt es dann den Blick hinter die Kulissen,&#8230;</p>
<p>The post <a href="https://creatronix.de/das-grose-git-tutorial/">Das große Git-Tutorial</a> appeared first on <a href="https://creatronix.de">Creatronix</a>.</p>
]]></description>
										<content:encoded><![CDATA[<h2>Motivation</h2>
<p>Jeder kennt und nutzt Git. Es gibt bis auf Ausnahmen (ich schaue dich an, Perforce) keine ernstzunehmenden Alternativen mehr.</p>
<p>Ich lasse Azubis ab der ersten Woche schon mit Git arbeiten, allerdings zunächst im Kontext einer IDE,<br />
damit sie ihren Code entspannt versionieren können.</p>
<p>Im zweiten Lehrjahr gibt es dann den Blick hinter die Kulissen, also auf die Kommandozeile.</p>
<h2>Inhalt</h2>
<ul>
<li>Basics: Installation, Konfiguration, Repository erstellen, Status &amp; Log, Commits, .gitignore, Remote, Fetch &amp; Pull</li>
<li>Intermediate: Branching, Merging, Merge-Konflikte, Stash, Diff, Tags</li>
<li>Advanced: Rebase, Squashen von Commits, Cherry-Pick, Reflog, Bisect</li>
</ul>
<h2>Voraussetzungen</h2>
<ul>
<li>Ein Terminal / eine Shell (bash, zsh, PowerShell, …)</li>
<li>Grundlegende Erfahrung mit der Kommandozeile (cd, mkdir, ls/dir)</li>
<li>Optional: ein GitHub- oder GitLab-Account für Remote-Repositories</li>
</ul>
<h2>Basics</h2>
<h3>Installation</h3>
<p>Damit etwas unter Versionskontrolle gestellt werden kann, muss Git zuerst installiert sein.</p>
<p><strong>Windows:</strong></p>
<p>Download von <a href="https://git-scm.com/download/win">https://git-scm.com/download/win</a> und Installer ausführen</p>
<p><strong>Linux (Debian/Ubuntu):</strong></p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-bash" data-lang="Bash"><code>sudo apt install git</code></pre>
</div>
<p><strong>macOS:</strong></p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-bash" data-lang="Bash"><code>brew install git</code></pre>
</div>
<p>Wir können nun testen, ob die Installation erfolgreich war:</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-bash" data-lang="Bash"><code>git --version</code></pre>
</div>
<h3>Konfiguration</h3>
<p>Bevor wir loslegen, teilen wir Git unseren Namen und unsere E-Mail-Adresse mit.<br />
Diese Infos werden in jedem Commit gespeichert.</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-bash" data-lang="Bash"><code>git config --global user.name "Dein Name"
git config --global user.email "deine@email.de"</code></pre>
</div>
<p>Optional – Standardbranch auf <code>main</code> setzen:</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-bash" data-lang="Bash"><code>git config --global init.defaultBranch main</code></pre>
</div>
<h3>Repository erstellen (<code>init</code>)</h3>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-bash" data-lang="Bash"><code>mkdir my_workspace
cd my_workspace
git init</code></pre>
</div>
<p>Damit haben wir ein leeres Git-Repository. Jetzt legen wir die ersten Dateien an:</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-bash" data-lang="Bash"><code>touch .gitignore
touch README.md
touch app.py</code></pre>
</div>
<h3>Status &amp; Log</h3>
<p>Zwei Befehle, die man ständig braucht sind:</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-bash" data-lang="Bash"><code>git status # Zeigt den aktuellen Zustand des Repositories
git log # Zeigt die Commit-Historie
git log --oneline # Kompakte Einzeiler-Ansicht</code></pre>
</div>
<h3>Staging &amp; Commit</h3>
<p>Dateien müssen zunächst in die Staging Area und können dann committet werden:</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-bash" data-lang="Bash"><code>git add . # Alle Änderungen stagen
git add README.md # Einzelne Datei stagen
git commit -m "initial commit" # Commit mit Nachricht</code></pre>
</div>
<p><strong>Tipp:</strong> Gute Commit-Messages beschreiben <em>was</em> und <em>warum</em>, nicht <em>wie</em>.</p>
<p>&nbsp;</p>
<h3><img fetchpriority="high" decoding="async" src="https://creatronix.de/wp-content/uploads/2026/03/git-init-add-commit.png" alt="" width="1010" height="275" class="alignnone size-full wp-image-7962" srcset="https://creatronix.de/wp-content/uploads/2026/03/git-init-add-commit.png 1010w, https://creatronix.de/wp-content/uploads/2026/03/git-init-add-commit-300x82.png 300w, https://creatronix.de/wp-content/uploads/2026/03/git-init-add-commit-768x209.png 768w" sizes="(max-width: 1010px) 100vw, 1010px" /></h3>
<h3>.gitignore</h3>
<p>Die Datei <code>.gitignore</code> definiert, welche Dateien Git ignorieren soll (Build-Artefakte, IDE-Konfiguration, Secrets, …):</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-json" data-lang="JSON"><code># Beispiel .gitignore
pycache/
*.pyc
.idea/
.env
build/
</code></pre>
</div>
<h3>Remote hinzufügen und pushen</h3>
<p>Ein Remote-Repository (z. B. auf GitHub) wird so verknüpft:</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-bash" data-lang="Bash"><code>git remote add origin https://github.com/user/repo.git
git push -u origin main</code></pre>
</div>
<p><code>-u</code> setzt das Upstream-Tracking, sodass <code>git push</code> danach ohne weitere Argumente funktioniert.</p>
<h3>Änderungen vom Remote holen</h3>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-bash" data-lang="Bash"><code>git fetch # Holt Änderungen, merged aber NICHT
git pull # Holt Änderungen UND merged in den aktuellen Branch</code></pre>
</div>
<p><strong>Faustregel:</strong> <code>fetch</code> ist sicher und unverbindlich, <code>pull</code> verändert den lokalen Branch.</p>
<h2>Intermediate</h2>
<h3>Remote-Repo clonen</h3>
<p>Statt ein Repository von Grund auf neu zu erstellen, kannst du ein bestehendes Remote-Repository mit <code>git clone</code> auf den eigenen Rechner kopieren.<br />
Dabei passiert automatisch einiges:</p>
<ol>
<li>Ein neuer Ordner mit dem Repository-Namen wird erstellt</li>
<li>Die gesamte Historie (alle Commits, Branches, Tags) wird heruntergeladen</li>
<li>Ein Remote namens <code>origin</code> wird angelegt, der auf die Quell-URL zeigt</li>
<li>Der Standard-Branch (z. B. <code>main</code>) wird ausgecheckt</li>
</ol>
<p><strong>Grundsyntax:</strong></p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-bash" data-lang="Bash"><code>git clone https://github.com/user/repo.git</code></pre>
</div>
<p><strong>Einen bestimmten Branch clonen:</strong></p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-bash" data-lang="Bash"><code>git clone -b develop https://github.com/user/repo.git</code></pre>
</div>
<p>Damit wird direkt der Branch <code>develop</code> ausgecheckt, nützlich, wenn du nicht auf <code>main</code> arbeiten möchtest.</p>
<p><strong>Tipp:</strong> Nach dem Clonen kannst du dir den eingerichteten Remote anzeigen lassen:</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-bash" data-lang="Bash"><code>cd repo
git remote -v

#origin https://github.com/user/repo.git (fetch)
#origin https://github.com/user/repo.git (push)</code></pre>
</div>
<p>&nbsp;</p>
<p>&nbsp;</p>
<h3><img decoding="async" src="https://creatronix.de/wp-content/uploads/2026/03/git-workflow.png" alt="" width="842" height="202" class="alignnone size-full wp-image-7961" srcset="https://creatronix.de/wp-content/uploads/2026/03/git-workflow.png 842w, https://creatronix.de/wp-content/uploads/2026/03/git-workflow-300x72.png 300w, https://creatronix.de/wp-content/uploads/2026/03/git-workflow-768x184.png 768w" sizes="(max-width: 842px) 100vw, 842px" /></h3>
<h3>Branching</h3>
<p>Branches sind einer der größten Vorteile von Git. Sie erlauben paralleles Arbeiten an Features, Bugfixes usw.</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-bash" data-lang="Bash"><code>git branch # Alle lokalen Branches anzeigen
git branch feature/login # Neuen Branch erstellen
git checkout feature/login # In den Branch wechseln
git checkout -b feature/login # Erstellen + Wechseln in einem Schritt</code></pre>
</div>
<h3><img decoding="async" src="https://creatronix.de/wp-content/uploads/2026/03/git-remote-branch.png" alt="" width="911" height="290" class="alignnone size-full wp-image-7963" srcset="https://creatronix.de/wp-content/uploads/2026/03/git-remote-branch.png 911w, https://creatronix.de/wp-content/uploads/2026/03/git-remote-branch-300x95.png 300w, https://creatronix.de/wp-content/uploads/2026/03/git-remote-branch-768x244.png 768w" sizes="(max-width: 911px) 100vw, 911px" /></h3>
<h3>Merging</h3>
<p>Wenn ein Feature fertig ist, wird der Branch zurück in <code>main</code> gemerged:</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-bash" data-lang="Bash"><code>git checkout main
git merge feature/login</code></pre>
</div>
<h3>Merge-Konflikte</h3>
<p>Wenn zwei Branches dieselbe Stelle in einer Datei geändert haben, entsteht ein Merge-Konflikt.<br />
Git markiert die betroffenen Stellen:</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-bash" data-lang="Bash"><code>&lt;&lt;&lt;&lt;&lt;&lt;&lt; HEAD
Deine Änderung
=======
Änderung aus dem anderen Branch
&gt;&gt;&gt;&gt;&gt;&gt;&gt; feature/login</code></pre>
</div>
<p>Lösung: Datei manuell bereinigen, dann:</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-bash" data-lang="Bash"><code>git add &lt;datei&gt;
git commit</code></pre>
</div>
<h3>Stash</h3>
<p>Unfertige Änderungen kann man zwischenspeichern, ohne zu committen:</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-bash" data-lang="Bash"><code>git stash # Änderungen wegstashen
git stash list # Alle Stashes anzeigen
git stash pop # Letzten Stash wiederherstellen und entfernen
git stash apply # Letzten Stash wiederherstellen, aber behalten</code></pre>
</div>
<h3>Diff</h3>
<p>Änderungen anzeigen lassen:</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-bash" data-lang="Bash"><code>git diff # Unstaged-Änderungen
git diff --staged # Staged-Änderungen (vor dem Commit)
git diff main..feature # Unterschied zwischen zwei Branches</code></pre>
</div>
<h3>Tags</h3>
<p>Releases oder Meilensteine markieren:</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-bash" data-lang="Bash"><code>git tag v1.0.0
git tag -a v1.0.0 -m "Erstes Release"
git push origin v1.0.0</code></pre>
</div>
<h2>Advanced</h2>
<h3>Rebase</h3>
<p>Rebase schreibt die Commit-Historie um und setzt Commits auf eine neue Basis.<br />
Nützlich, um einen sauberen, linearen Verlauf zu erzeugen:</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-bash" data-lang="Bash"><code>git checkout feature/login
git rebase main</code></pre>
</div>
<p>&nbsp;</p>
<p><strong><img decoding="async" src="https://creatronix.de/wp-content/uploads/2026/03/git-rebase.png" alt="" width="685" height="202" class="alignnone size-full wp-image-7964" srcset="https://creatronix.de/wp-content/uploads/2026/03/git-rebase.png 685w, https://creatronix.de/wp-content/uploads/2026/03/git-rebase-300x88.png 300w" sizes="(max-width: 685px) 100vw, 685px" /></strong></p>
<p><strong>Achtung:</strong> Rebase verändert die Historie. Deshalb nicht auf bereits gepushten, geteilten Branches verwenden.</p>
<h3>Squashen von Commits</h3>
<p>Mehrere Commits zu einem zusammenfassen (z. B. vor einem Merge Request):</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-bash" data-lang="Bash"><code>git rebase -i HEAD~3 # Die letzten 3 Commits interaktiv bearbeiten</code></pre>
</div>
<p>Im Editor dann <code>pick</code> für die gewünschten Commits durch <code>squash</code> (oder <code>s</code>) ersetzen.</p>
<h3>Cherry-Pick</h3>
<p>Einen einzelnen Commit aus einem anderen Branch übernehmen:</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-bash" data-lang="Bash"><code>git cherry-pick &lt;commit-hash&gt;</code></pre>
</div>
<h3>Reflog</h3>
<p>Das Sicherheitsnetz: Zeigt <em>alle</em> Aktionen, auch nach einem versehentlichen Reset:</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-bash" data-lang="Bash"><code>git reflog
git checkout &lt;hash&gt; # Verlorenen Stand wiederherstellen</code></pre>
</div>
<h2>Fazit</h2>
<p>Git ist ein mächtiges Werkzeug. Die Basics reichen für den Alltag, aber wer Rebase, Squash und Bisect beherrscht,<br />
kann auch in komplexen Projekten souverän arbeiten.</p>
<p><strong>Weiterführende Ressourcen:</strong></p>
<ul>
<li><a href="https://learngitbranching.js.org/?locale=de_DE">Git Branching interaktiv lernen</a></li>
<li><a href="https://git-scm.com/book/de/v2">Pro Git Book (kostenlos)</a></li>
<li><a href="https://education.github.com/git-cheat-sheet-education.pdf">Git Cheat Sheet</a></li>
<li><code>git help &lt;befehl&gt;</code>: Die eingebaute Hilfe ist besser, als man denkt</li>
</ul>
<p>The post <a href="https://creatronix.de/das-grose-git-tutorial/">Das große Git-Tutorial</a> appeared first on <a href="https://creatronix.de">Creatronix</a>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Lerne neue Programmiersprachen schneller mit diesen Coding Challenges</title>
		<link>https://creatronix.de/lerne-neue-programmiersprachen-schneller-mit-diesen-coding-challenges/</link>
		
		<dc:creator><![CDATA[Jörn]]></dc:creator>
		<pubDate>Sun, 22 Mar 2026 07:20:50 +0000</pubDate>
				<category><![CDATA[Software Engineering & Programming]]></category>
		<guid isPermaLink="false">https://creatronix.de/?p=7954</guid>

					<description><![CDATA[<p>Motivation Motivation Das Erlernen einer Programmiersprache kann herausfordernd sein. Es gibt zwar unzählige Artikel und Tutorials, doch echtes Verständnis entsteht vor allem durch regelmäßige Wiederholung und praktische Anwendung. Wer Konzepte immer wieder in kleinen Übungen, Katas und realen Problemen einsetzt, entwickelt mit der Zeit eine Art Muscle Memory fürs Programmieren. Genau diese Routine hilft dabei,&#8230;</p>
<p>The post <a href="https://creatronix.de/lerne-neue-programmiersprachen-schneller-mit-diesen-coding-challenges/">Lerne neue Programmiersprachen schneller mit diesen Coding Challenges</a> appeared first on <a href="https://creatronix.de">Creatronix</a>.</p>
]]></description>
										<content:encoded><![CDATA[<h2>Motivation</h2>
<h2>Motivation</h2>
<p>Das Erlernen einer Programmiersprache kann herausfordernd sein. Es gibt zwar unzählige Artikel und Tutorials, doch echtes Verständnis entsteht vor allem durch regelmäßige Wiederholung und praktische Anwendung.</p>
<p>Wer Konzepte immer wieder in kleinen Übungen, Katas und realen Problemen einsetzt, entwickelt mit der Zeit eine Art Muscle Memory fürs Programmieren. Genau diese Routine hilft dabei, Syntax, Denkweisen und Lösungsstrategien nachhaltig zu verinnerlichen.</p>
<h2>Die Herausforderungen</h2>
<p>Die Programmieraufgaben sind sprachunabhängig, sodass du jede moderne Sprache verwenden kannst, die das Schreiben von Konsolenanwendungen unterstützt.</p>
<h2>🟢 Level 1 – Grundlagen (Syntax &amp; einfache Logik)</h2>
<ol>
<li>Schreibe das berühmte / berüchtigte „Hello World“</li>
<li>Schreibe ein Skript, das alle Zahlen von 1 bis 365 in die Konsole ausgibt</li>
<li>Schreibe ein Skript, das 10 Zufallszahlen zwischen 1 und 10 ausgibt</li>
<li>Schreibe ein Skript, das alle Buchstaben des Alphabets (großgeschrieben) mit ihrer jeweiligen Position ausgibt, z. B. 1: A, 2: B</li>
<li>Schreibe deinen ersten Unit-Test für eine Funktion <code>is_even(num)</code>, die <code>true</code> zurückgibt, wenn die Zahl gerade ist</li>
</ol>
<h2>🟡 Level 2 – Einfache Funktionen &amp; Kontrolle</h2>
<ol>
<li>Schreibe eine Funktion <code>to_upper</code>, die einen String von lower_case nach upper_case umwandelt</li>
<li>Die Funktion <code>compare(num1, num2)</code> soll
<ol>
<li>-1 zurückgeben, wenn <code>num1</code> kleiner als <code>num2</code> ist</li>
<li>ansonsten 1 zurückgeben</li>
<li>wenn beide Werte gleich sind, 0 zurückgeben</li>
</ol>
</li>
<li>Die Funktion <code>simple_sum(num)</code> soll alle Zahlen von 1 bis <code>num</code> aufsummieren
<ol>
<li>Beispiel: Bei Eingabe 4 soll das Ergebnis 10 sein</li>
<li>Für Testfälle liegt <code>num</code> zwischen 1 und 1000</li>
</ol>
</li>
<li>Die Funktion <code>time_convert(minutes)</code> soll Minuten in Stunden und Minuten umwandeln (z. B. 63 → 1:03)</li>
<li>Die Funktion <code>first_reverse(input)</code> soll den String umdrehen</li>
</ol>
<h2>🟠 Level 3 – Strings &amp; Arrays</h2>
<ol>
<li>Die Funktion <code>alphabet_soup(input)</code> soll den String alphabetisch sortieren</li>
<li>Die Funktion <code>longest_word(sentence)</code> soll das längste Wort zurückgeben</li>
<li>Die Funktion <code>letter_changes(input)</code> soll Buchstaben ersetzen und Vokale kapitalisieren</li>
</ol>
<h2>🔵 Level 4 – Mathematische Grundlagen</h2>
<ol>
<li>Die Funktion <code>factorial(num)</code> soll die Fakultät berechnen</li>
<li>Schreibe eine Funktion, die Fibonacci-Zahlen erzeugt (inkl. Memoization)</li>
<li>Schreibe eine Funktion, die die Collatz-Sequenz als Liste zurückgibt</li>
<li>Schreibe eine Funktion, die den Mittelwert (Mean) eines Integer-Arrays berechnet</li>
<li>Schreibe eine Funktion, die den Median eines Integer-Arrays berechnet</li>
</ol>
<h2>🟣 Level 5 – Klassische Algorithmen</h2>
<ol>
<li>Schreibe eine Funktion, die ein Integer-Array mit Bubble Sort sortiert</li>
<li>Schreibe eine Funktion, die eine Liste von Primzahlen mithilfe des Siebs des Eratosthenes zurückgibt</li>
<li>Schreibe eine Funktion, die den größten gemeinsamen Teiler mithilfe des euklidischen Algorithmus berechnet</li>
</ol>
<h2>🔴 Level 6 – Anspruchsvoll / Interview-Level</h2>
<ol>
<li>Schreibe eine Funktion, die römische Zahlen in Dezimalzahlen konvertiert</li>
<li>Schreibe eine Funktion, die Dezimalzahlen in römische Zahlen konvertiert</li>
<li>Die Funktion <code>kaprekars_constant(num)</code> berechnet die Anzahl der Schritte bis 6174 erreicht wird</li>
</ol>
<h2>🧪 Hinweis</h2>
<p>Ab der Funktion <code>simple_sum</code> sollte nach dem TDD-Prinzip gearbeitet werden (Test → Implementierung → Refactoring).</p>
<p>The post <a href="https://creatronix.de/lerne-neue-programmiersprachen-schneller-mit-diesen-coding-challenges/">Lerne neue Programmiersprachen schneller mit diesen Coding Challenges</a> appeared first on <a href="https://creatronix.de">Creatronix</a>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Was ist Software Engineering? Eine verständliche Einführung</title>
		<link>https://creatronix.de/was-ist-software-engineering-eine-verstaendliche-einfuehrung/</link>
		
		<dc:creator><![CDATA[Jörn]]></dc:creator>
		<pubDate>Sat, 21 Mar 2026 12:41:56 +0000</pubDate>
				<category><![CDATA[Software Engineering & Programming]]></category>
		<guid isPermaLink="false">https://creatronix.de/?p=7932</guid>

					<description><![CDATA[<p>Software Engineering ist die systematische Entwicklung von Software: von der Idee über die Umsetzung bis zum Betrieb. Programmieren gehört natürlich dazu, ist aber interessanterweise oft nur ein kleiner Teil des Ganzen. Disclaimer Die folgende Unterteilung in Programmierung, Entwicklung und Engineering ist ein Versuch einer Einordnung von Prozessen, Methoden und Tools. Ich habe dieses Modell entwickelt,&#8230;</p>
<p>The post <a href="https://creatronix.de/was-ist-software-engineering-eine-verstaendliche-einfuehrung/">Was ist Software Engineering? Eine verständliche Einführung</a> appeared first on <a href="https://creatronix.de">Creatronix</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p>Software Engineering ist die systematische Entwicklung von Software: von der Idee über die Umsetzung bis zum Betrieb. Programmieren gehört natürlich dazu, ist aber interessanterweise oft nur ein kleiner Teil des Ganzen.</p>
<h2>Disclaimer</h2>
<p>Die folgende Unterteilung in Programmierung, Entwicklung und Engineering ist ein Versuch einer Einordnung von Prozessen, Methoden und Tools. Ich habe dieses Modell entwickelt, um unseren Auszubildenden und Praktikanten, die oft keine Vorkenntnisse hatten, Software-Engineering beizubringen.</p>
<p>Daher enthält es einige persönliche Ansichten und möglicherweise keine präzisen Definitionen. Es spiegelt meine fast 20-jährige Erfahrung in der Softwarebranche als Entwickler, Tester, QA-Mitarbeiter und Teamleiter wider.</p>
<h2><img decoding="async" src="https://creatronix.de/wp-content/uploads/2023/01/mental-model.png" alt="" width="391" height="392" class="alignnone size-full wp-image-6606" srcset="https://creatronix.de/wp-content/uploads/2023/01/mental-model.png 391w, https://creatronix.de/wp-content/uploads/2023/01/mental-model-300x300.png 300w, https://creatronix.de/wp-content/uploads/2023/01/mental-model-150x150.png 150w" sizes="(max-width: 391px) 100vw, 391px" /></h2>
<h2>Programmierung: der Kern</h2>
<p>Ein bekannter Spruch lautet:</p>
<blockquote><p>A programmer is an organism that turns coffee and pizza into code.</p></blockquote>
<p>Programmieren bedeutet, Anforderungen des Kunden in ausführbare Anweisungen, die ein Computer versteht, zu übersetzen. Das kann eine einzelne Person — ein Programmierer — tun.</p>
<h3>Programmiersprachen</h3>
<p>Ein Programmierer braucht eine oder mehrere Programmiersprachen um <strong>Quellcode</strong> zu erstellen. Quellcode oder oft nur <strong>Code</strong> ist Text in einer Sprache wie C, <a href="https://creatronix.de/modernes-c-plus-plus/">C++</a>, Java oder <a href="https://creatronix.de/python-tips-tricks-for-junior-developers/">Python</a>. Jede Sprache definiert klare Regeln für Syntax und Semantik.</p>
<h3>Vom Quelltext zum Programm</h3>
<ol>
<li>Quelltext schreiben (Editor oder IDE)</li>
<li>Kompilieren (z. B. C/C++)</li>
<li>Linken und ausführbare Datei erzeugen</li>
</ol>
<p>Beispiel in C:</p>
<div>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-c" data-lang="C"><code>#include &lt;stdio.h&gt;

int main(void) {
    printf("Hello world\n");
    return 0;
}</code></pre>
</div>
</div>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-bash" data-lang="Bash"><code>gcc -Wall -g -c hello.c
gcc -o hello hello.o -lm</code></pre>
</div>
<p>&nbsp;</p>
<p><img decoding="async" src="https://creatronix.de/wp-content/uploads/2026/03/compiler_linker-1024x150.png" alt="" width="1024" height="150" class="alignnone size-large wp-image-7929" srcset="https://creatronix.de/wp-content/uploads/2026/03/compiler_linker-1024x150.png 1024w, https://creatronix.de/wp-content/uploads/2026/03/compiler_linker-300x44.png 300w, https://creatronix.de/wp-content/uploads/2026/03/compiler_linker-768x112.png 768w, https://creatronix.de/wp-content/uploads/2026/03/compiler_linker.png 1031w" sizes="(max-width: 1024px) 100vw, 1024px" /></p>
<p>Das reicht schon, damit wurde bereits ein Programm erstellt. Für echte Projekte oder Produkte reicht das reine<br />
Programmieren aber noch nicht aus.</p>
<h2>Softwareentwicklung: vom Code zum Produkt</h2>
<p>Sobald mehrere Personen beteiligt sind oder Kundennutzen im Fokus steht, kommen Prozesse, Rollen und Werkzeuge hinzu.</p>
<h3>Requirements Tracking &#8211; Anforderungen erfassen und klären</h3>
<p>Damit ein Team von Programmierern das Richtige baut, müssen Anforderungen des Kunden klar beschrieben werden. Eine Anforderung, die in Textform niedergeschrieben wurde, nennt man auch <strong>Requirement</strong>. Für die Formulierung haben sich zwei Formate etabliert:</p>
<ul>
<li>User Story: Als <strong>Rolle</strong> möchte ich <strong>Funktion</strong>, damit <strong>Nutzen</strong>.</li>
<li>Use Case: strukturierte Beschreibung einer Interaktion zwischen Nutzer und System</li>
</ul>
<p>Anforderungen erhalten IDs, Status und Verweise auf Implementierung und Tests. So bleibt über den gesamten Lebenszyklus nachvollziehbar, was umgesetzt, geändert oder verworfen wurde.</p>
<p>Auch die guten alten Lastenhefte (Was der Kunde will) und Pflichtenhefte (Was wir verstanden haben und auch umsetzen werden) haben oft noch ihre Daseinsberechtigung.</p>
<h3>Neue Rollen</h3>
<p>Dedizierte Tester entlasten ein Team von Programmierern und erhöhen die Produktqualität, weil sie Programme systematisch aus Nutzer- und Risikoperspektive prüfen.</p>
<p>Typische Aufgaben für Tester sind:</p>
<ul>
<li>Testfälle aus den Anforderungen ableiten</li>
<li>Teststrategie und Testdaten definieren</li>
<li>Manuelle und automatisierte Tests durchführen</li>
<li>Fehler sauber dokumentieren und priorisieren</li>
<li>Regressionsrisiken vor Releases bewerten</li>
</ul>
<p>Wichtig: Tester &#8220;testen nicht nur am Ende&#8221;, sondern arbeiten <a href="https://creatronix.de/wie-baue-ich-eine-entwicklungsbegleitende-software-qualitatssicherung-auf/">früh</a> mit den Programmierern zusammen. So werden Qualitätsprobleme früher sichtbar und deutlich günstiger behoben.</p>
<h3>Dokumentation</h3>
<p>Gute <strong>Dokumentation</strong> entlastet Teams im Alltag und reduziert Wissensinseln.</p>
<ul>
<li>Produktdokumentation: Was kann das System aus Nutzersicht?</li>
<li>Technische Dokumentation: Architektur, Schnittstellen, Betriebswissen</li>
<li>Entscheidungsdokumentation: Warum wurde etwas so gebaut?</li>
</ul>
<p>Wichtig ist nicht &#8220;möglichst viel Text&#8221;, sondern aktuelle, auffindbare und wartbare Dokumentation.</p>
<h3>Versionsverwaltung nutzen</h3>
<p>Werkzeuge wie <a href="https://creatronix.de/das-grose-git-tutorial/">Git</a> speichern den kompletten zeitlichen Verlauf einer Codebasis. Sie ermöglichen damit:</p>
<ul>
<li>Zusammenarbeit im Team</li>
<li>Nachvollziehbare Änderungen</li>
<li>Rückkehr zu früheren Ständen</li>
</ul>
<p>Hinweis: Git ersetzt kein vollständiges Backup-Konzept.</p>
<h3><img decoding="async" src="https://creatronix.de/wp-content/uploads/2026/03/branching-1024x391.png" alt="" width="1024" height="391" class="alignnone wp-image-7931 size-large" srcset="https://creatronix.de/wp-content/uploads/2026/03/branching-1024x391.png 1024w, https://creatronix.de/wp-content/uploads/2026/03/branching-300x115.png 300w, https://creatronix.de/wp-content/uploads/2026/03/branching-768x293.png 768w, https://creatronix.de/wp-content/uploads/2026/03/branching.png 1194w" sizes="(max-width: 1024px) 100vw, 1024px" /></h3>
<h3>Builds automatisieren &amp; Abhängigkeiten verwalten</h3>
<p>Moderne Anwendungen nutzen viele oft externe Bibliotheken. Package Manager wie <code>cargo</code>, <code>npm</code> oder <code>gradle</code> helfen bei einheitlichen Builds und reproduzierbaren Versionen. Ziel: Alle können jederzeit denselben Build erzeugen.</p>
<p>Zusätzlich braucht es klare <strong>Build Tools</strong> und ein belastbares <strong>Build System</strong>:</p>
<ul>
<li><a href="https://creatronix.de/compiler-optionen-fur-sauberes-c/">standardisierte Build-Kommandos</a> lokal und in CI</li>
<li>reproduzierbare Artefakte über Umgebungen hinweg</li>
<li>schnelle, stabile Pipelines mit klarer Fehlerdiagnose</li>
</ul>
<h3>IDE</h3>
<p>Während beim reinen Programmieren ein einfacher Texteditor ausreichen kann, verwenden Softwareentwickler in der Praxis meist leistungsfähige IDEs.</p>
<p>Eine IDE bündelt zentrale Entwicklungsaufgaben in einem Werkzeug:</p>
<ul>
<li>Code schreiben mit Syntaxprüfung und Autovervollständigung</li>
<li>Navigation durch große Codebasen (z. B. &#8220;Gehe zu Definition&#8221;)</li>
<li>Refactoring mit geringem Fehlerrisiko</li>
<li>Debugging mit Breakpoints und Variableninspektion</li>
<li>Direkte Integration von Tests, Linter und Build-Prozessen</li>
<li>Anbindung an Versionsverwaltung wie Git</li>
</ul>
<p>Der eigentliche Nutzen liegt in der Produktivität und Qualität: Teams arbeiten schneller, konsistenter und machen weniger vermeidbare Fehler.</p>
<h3>Issue Tracking &#8211; Arbeit transparent machen</h3>
<p>Issues, Bugs und Aufgaben werden in einem Tracking-System verwaltet (z. B. Jira, YouTrack, GitHub Issues). So bleiben Prioritäten, Zuständigkeiten und Fortschritt nachvollziehbar.</p>
<h3>Projektorganisation mit Scrum oder Kanban</h3>
<p>Damit Teamarbeit planbar und transparent bleibt, wird Arbeit in kleine, priorisierte Einheiten aufgeteilt und sichtbar gemacht.</p>
<p>Bei <strong>Scrum</strong> erfolgt die Planung meist in festen Iterationen (Sprints), z. B. alle ein bis zwei Wochen:</p>
<ul>
<li>Ziele für den Sprint festlegen</li>
<li>Aufgaben gemeinsam schätzen und zuschneiden</li>
<li>Fortschritt täglich abstimmen</li>
<li>Ergebnisse am Sprint-Ende überprüfen und verbessern</li>
</ul>
<p>Bei <strong>Kanban</strong> steht der kontinuierliche Fluss im Vordergrund:</p>
<ul>
<li>Aufgaben visualisieren (z. B. To-do, In Arbeit, Erledigt)</li>
<li>Parallelarbeit mit WIP-Limits begrenzen</li>
<li>Engpässe früh erkennen und aktiv beseitigen</li>
</ul>
<p>Beide Ansätze helfen, Lieferfähigkeit und Qualität im Alltag stabil zu halten.</p>
<h3>Qualität absichern</h3>
<p>Qualität entsteht nicht zufällig, sondern durch disziplinierte Routinen:</p>
<ul>
<li>Tests (Unit, Integration, End-to-End)</li>
<li><a href="https://creatronix.de/wie-tdd-die-welt-rettet-und-deine-nerven-schont/">TDD</a> (Test-Driven Development)</li>
<li>Debugging</li>
<li><a href="https://creatronix.de/logging-in-python-cheat-sheet/">Logging</a></li>
<li>Linting und statische Codeanalyse</li>
<li>Code Reviews</li>
</ul>
<h4>TDD sinnvoll einsetzen</h4>
<p>Bei TDD schreibst du zuerst einen fehlschlagenden Test, implementierst dann den minimal nötigen Code und verbesserst anschließend die Struktur (Red-Green-Refactor).</p>
<p>TDD ist besonders hilfreich bei:</p>
<ul>
<li>Geschäftslogik mit klaren Regeln</li>
<li>APIs und Modulen mit stabilen Schnittstellen</li>
<li>Codebereichen, die häufig geändert werden</li>
</ul>
<p>So entsteht oft besser testbarer, klarer strukturierter Code und Regressionen werden früher erkannt.</p>
<h3>Struktur schaffen mit Design Patterns</h3>
<p>Design Patterns werden benötigt, weil in Softwareprojekten viele Probleme immer wieder auftreten. Statt jedes Mal eine neue, ungeprüfte Lösung zu bauen, nutzt du bewährte Strukturen.</p>
<p>Sie helfen konkret bei:</p>
<ul>
<li>Verständlichkeit: Andere Entwickler erkennen schneller, wie dein Code aufgebaut ist.</li>
<li>Wartbarkeit: Änderungen sind einfacher, weil Verantwortlichkeiten klarer getrennt sind.</li>
<li>Erweiterbarkeit: Neue Features lassen sich oft mit weniger Umbau integrieren.</li>
<li>Teamkommunikation: Begriffe wie Factory, Observer oder Strategy sind eine gemeinsame Sprache.</li>
<li>Risikoreduktion: Patterns sind praxiserprobt und vermeiden typische Architekturfehler.</li>
</ul>
<p>Kurz: Design Patterns machen Code nicht &#8220;magisch besser&#8221;, aber sie machen komplexe Systeme robuster und für Teams beherrschbarer.</p>
<h2>Software-Engineering &#8211; erfolgreich Großprojekte umsetzen</h2>
<p>Sobald mehrere Teams parallel an einem Produkt arbeiten, reicht reine Softwareentwicklung nicht mehr aus. Spätestens bei Teamgrößen von etwa 8 bis 12 Personen (oder mehreren unabhängigen Feature-Teams) steigen Koordinationsaufwand, Integrationsrisiken und Betriebsverantwortung deutlich.</p>
<p>Dann geht es nicht nur um &#8220;Features bauen&#8221;, sondern auch um:</p>
<ul>
<li>einheitliche technische Leitplanken</li>
<li>verlässliche Release- und Betriebsprozesse</li>
<li>klare Verantwortlichkeiten zwischen Entwicklung, Produkt und Betrieb</li>
<li>skalierbare Qualitätssicherung über Teamgrenzen hinweg</li>
</ul>
<p>Genau hier beginnt der eigentliche Mehrwert von Software Engineering.</p>
<h3>Neue Rollen</h3>
<p>Mit wachsender Produkt- und Teamgröße entstehen zusätzliche Rollen, die Entwicklung strukturieren und skalierbar machen.</p>
<p><strong>Projektleiter</strong> koordinieren Zeit, Budget, Abhängigkeiten und Risiken.<br />
Sie sorgen dafür, dass Entscheidungen transparent sind und Teams in die gleiche Richtung arbeiten.</p>
<p><strong>Requirements Engineers</strong> präzisieren Anforderungen und übersetzen fachliche Ziele in umsetzbare Spezifikationen.<br />
Dadurch sinken Missverständnisse zwischen Fachseite, Produktmanagement und Entwicklung.</p>
<p><strong>DevOps-Teams</strong> bauen die Brücke zwischen Entwicklung und Betrieb.<br />
Sie verantworten u. a. CI/CD-Pipelines, Deployment-Prozesse, Monitoring und stabile Betriebsabläufe.</p>
<p>Wichtig ist das Zusammenspiel: Diese Rollen ersetzen nicht die Entwicklung, sondern schaffen den Rahmen, in dem Entwicklung zuverlässig liefern kann.</p>
<h3>Architektur definieren</h3>
<p>Architektur beschreibt die grobe Struktur und die Kommunikationswege eines Systems, z. B.:</p>
<ul>
<li>Frontend &#8211; Backend &#8211; Datenbank</li>
<li>Schichtenarchitektur</li>
<li>Event-basierte Systeme (Pub/Sub)</li>
<li>Dependency-Injection-Frameworks</li>
</ul>
<p>Sie legt die Leitplanken fest, nicht jedes Implementierungsdetail.</p>
<h3>Software betreiben und ausliefern</h3>
<p>Zum Engineering gehört auch der Betrieb:</p>
<ul>
<li>CI/CD (Continuous Integration / Continuous Deployment) für automatisches Bauen, Testen und Ausrollen</li>
<li>Staging-Umgebungen vor Produktion</li>
<li><a href="https://learngitbranching.js.org/?locale=de_DE">Branching-Strategie</a> (z. B. Trunk-Based oder Gitflow)</li>
<li>Containerisierung mit Docker/Kubernetes</li>
<li>Monitoring und Qualitätsmetriken</li>
<li>Release Notes pro Version oder Deployment</li>
<li>Artifact Mirror/Artifact Repository für kontrollierte Abhängigkeiten und Build-Artefakte</li>
</ul>
<h3>Quality Dashboards nutzen</h3>
<p><strong>Quality Dashboards</strong> machen Qualität messbar statt gefühlt. Typische Kennzahlen sind:</p>
<ul>
<li>Testabdeckung und Fehlerraten</li>
<li>Build- und Deployment-Stabilität</li>
<li>Laufzeitmetriken wie Latenz, Fehlerquote und Verfügbarkeit</li>
<li>technische Schulden (z. B. statische Analyse, Duplikate, Komplexität)</li>
</ul>
<p>Nicht jede Metrik passt für jedes Team. Entscheidend ist, wenige Kennzahlen konsequent zu nutzen und Trends zu beobachten.</p>
<h3>Blindspots im Alltag</h3>
<p>Einige Themen fehlen in vielen Teams anfangs, sind aber zentral für professionelles Software Engineering:</p>
<ul>
<li>Security by Design: Threat Modeling, Dependency-Scans, Secret-Management, Sicherheitsupdates</li>
<li>Reliability Engineering: SLOs, Alerting, Runbooks, Incident- und Postmortem-Prozesse</li>
<li>Wartbarkeit: technischer Schuldenabbau, regelmäßige Refactorings, klare Ownership</li>
<li>Wissensverteilung: Onboarding, Pairing, Reviews und Dokumentation statt &#8220;Single Points of Failure&#8221;</li>
</ul>
<h2>Fazit</h2>
<p>Software Engineering ist mehr als Programmieren. Es verbindet Technik, Prozesse und Zusammenarbeit, damit Software langfristig zuverlässig, wartbar und nutzbar bleibt.</p>
<p>Wie ich eingangs erwähnte: Dies ist ein gedankliches Modell. Die Grenzen zwischen den Bereichen sind fließend: Man kann natürlich von Anfang an Versionskontrolle einsetzen und ein CI/CD-System für ein einfaches „Hello World“-Programm einrichten. Mein Hauptanliegen ist es, das Bewusstsein dafür zu schärfen, dass Softwareentwicklung eine Vielzahl von Werkzeugen und Praktiken umfasst, deren Lernkurve steil ist, wenn man versucht, alles auf einmal zu beherrschen. Durch die Unterteilung des Fachgebiets in drei Ebenen lassen sich die Konzepte möglicherweise leichter schrittweise erfassen und die Fähigkeiten von innen nach außen entwickeln.</p>
<h2>Links</h2>
<p><a href="https://creatronix.de/die-junior-und-die-senior-rolle/">Die Junior und die Senior-Rolle</a></p>
<p><a href="https://creatronix.de/wie-baue-ich-eine-entwicklungsbegleitende-software-qualitatssicherung-auf/">Wie baue ich eine entwicklungsbegleitende Software-Qualitätssicherung auf?</a></p>
<p><a href="https://creatronix.de/wie-tdd-die-welt-rettet-und-deine-nerven-schont/">Wie TDD die Welt rettet und deine Nerven schont</a></p>
<p><a href="https://creatronix.de/pytest-tutorial/">Pytest Tutorial</a></p>
<p><a href="https://creatronix.de/so-verwendest-du-google-test-in-deinem-cpp-projekt/">So verwendest du Google Test in deinem C++-Projekt</a></p>
<p><a href="https://creatronix.de/alles-was-du-ueber-scrum-wissen-musst/">Alles was du über Scrum wissen musst</a></p>
<p><a href="https://creatronix.de/writing-as-complementary-skill-for-your-sw-engineering-career/">Writing as a complementary skill for your SW engineering career</a></p>
<h2>Literatur</h2>
<p><a href="https://amzn.to/40E6sBJ">The Software Engineer&#8217;s Guidebook</a></p>
<p><a href="https://frankwestphal.de/ftp/Westphal_Testgetriebene_Entwicklung.pdf">Mein erster Kontakt mit TDD (old but gold) </a></p>
<p>The post <a href="https://creatronix.de/was-ist-software-engineering-eine-verstaendliche-einfuehrung/">Was ist Software Engineering? Eine verständliche Einführung</a> appeared first on <a href="https://creatronix.de">Creatronix</a>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>So verwaltest du deine Roboter mit Ansible</title>
		<link>https://creatronix.de/so-verwaltest-du-deine-roboter-mit-ansible/</link>
		
		<dc:creator><![CDATA[Jörn]]></dc:creator>
		<pubDate>Wed, 11 Mar 2026 19:43:10 +0000</pubDate>
				<category><![CDATA[Robotics]]></category>
		<category><![CDATA[Software Engineering & Programming]]></category>
		<guid isPermaLink="false">https://creatronix.de/?p=7893</guid>

					<description><![CDATA[<p>Motivation Eine einzelne Ubuntu-Maschine zu warten, ist noch überschaubar. Man loggt sich per SSH ein, installiert ein paar Pakete, macht ein Update – und dann läuft die Kiste wieder. In der Robotik bleibt es aber selten bei einer Maschine. Sobald mehrere Roboter im Einsatz sind, sitzt man plötzlich vor einem kleinen Zoo aus Linux-Systemen. Dann&#8230;</p>
<p>The post <a href="https://creatronix.de/so-verwaltest-du-deine-roboter-mit-ansible/">So verwaltest du deine Roboter mit Ansible</a> appeared first on <a href="https://creatronix.de">Creatronix</a>.</p>
]]></description>
										<content:encoded><![CDATA[<h2>Motivation</h2>
<p>Eine einzelne Ubuntu-Maschine zu warten, ist noch überschaubar. Man loggt sich per SSH ein, installiert ein paar Pakete, macht ein Update – und dann läuft die Kiste wieder.</p>
<p>In der Robotik bleibt es aber selten bei einer Maschine. Sobald mehrere Roboter im Einsatz sind, sitzt man plötzlich vor einem kleinen Zoo aus Linux-Systemen.</p>
<p>Dann beginnt die klassische Runde:<br />
SSH auf Rechner eins, Updates einspielen.<br />
SSH auf Rechner zwei, dieselben Updates einspielen.<br />
SSH auf Rechner drei – na, Du ahnst es schon.</p>
<p>Spätestens nach dem fünften Roboter merkt man: Das ist weniger Systemadministration und mehr Fleißarbeit mit Copy-Paste.</p>
<p>Genau hier kommt Ansible ins Spiel. Mit sogenannten Playbooks beschreibt man einfach den gewünschten Zustand der Systeme – welche Pakete installiert sein sollen, welche Konfigurationen gelten, welche Updates eingespielt werden. Ansible kümmert sich dann darum, diese Zustände automatisch auf allen Zielrechnern umzusetzen.</p>
<p>Gerade in Robotik-Umgebungen mit vielen verteilten Rechnern sorgt dieser Ansatz dafür, dass Wartung nicht mehr Handarbeit ist – sondern planbar, reproduzierbar und vor allem deutlich entspannter.</p>
<h2>Ansible</h2>
<p>Ansible wurde 2012 von Michael DeHaan entwickelt und als Open-Source-Projekt veröffentlicht. 2015 wurde das Unternehmen Ansible, Inc. von Red Hat übernommen, das wiederum seit 2019 zu IBM gehört.</p>
<p>Es ist ein agentenloses Automatisierungstool – das heißt, auf den Zielrechnern muss keine zusätzliche Software installiert werden. Ansible verbindet sich einfach per SSH und führt die gewünschten Aufgaben aus.</p>
<h2>Setup</h2>
<p>Für dieses Tutorial brauchst Du einen Ubuntu-PC und einen Raspberry Pi mit einem installierten Ubuntu. Auf deinem Control-Rechner (also deinem PC) läuft Ansible und der SSH Client, auf dem Raspberry (also dem Remote-Rechner) muss der SSH-Server aktiviert sein.</p>
<h2>Installation</h2>
<p>Auf deinem Control-Rechner installierst du Ansible wie folgt:</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-bash" data-lang="Bash"><code>sudo apt update
sudo apt install ansible
sudo apt install sshpass</code></pre>
</div>
<p>Jetzt kannst Du testen, ob die Installation geklappt hat:</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-bash" data-lang="Bash"><code>ansible --version</code></pre>
</div>
<h2>SSH aktivieren</h2>
<p>Damit Ansible funktioniert, muss auf dem Raspberry aka Remote-Rechner SSH eingerichtet sein.<br />
Probier mal im Terminal deines Control-Rechners aus, dich mit dem Raspberry zu verbinden:</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-bash" data-lang="Bash"><code>ssh pi@192.168.178.50</code></pre>
</div>
<p>Falls das nicht klappt, musst du SSH auf dem Raspberry aktivieren. Das geht ganz einfach über die raspi-config:</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-bash" data-lang="Bash"><code>sudo raspi-config</code></pre>
</div>
<p>Dann navigierst du zu &#8220;Interfacing Options&#8221; -&gt; &#8220;SSH&#8221; und aktivierst den SSH-Server.</p>
<h2>Projektordner</h2>
<p>Auf deinem Control-Rechner legst du dir z.B. einen Projektordner an:</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-bash" data-lang="Bash"><code>mkdir ~/ansible
cd ansible
touch hosts
touch ansible.cfg
touch update_pi.yml</code></pre>
</div>
<p>Die Ordnerstruktur soll nachher so aussehen:</p>
<p>ansible/<br />
├── hosts<br />
├── update_pi.yml<br />
└── ansible.cfg</p>
<h2>hosts-Datei anlegen</h2>
<p>In der hosts-Datei stehen die Rechner und ihre IP-Adressen bzw. ihre A-Records.</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-json" data-lang="JSON"><code>[raspberry]
pi1 ansible_host=192.168.178.66 ansible_user=&lt;username&gt;</code></pre>
</div>
<h2>ansible.cfg</h2>
<p>Die ansible.cfg Datei ist die Konfigurationsdatei für Ansible.<br />
Hier kannst du verschiedene Einstellungen vornehmen, z.B. den Pfad zu deiner hosts-Datei.<br />
Ein einfaches Beispiel sieht so aus</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-json" data-lang="JSON"><code>[defaults]
inventory = hosts</code></pre>
</div>
<h2>Playbooks</h2>
<p>Playbooks sind die Ansible-Äquivalente zu Skripten, sie beschreiben die gewünschten Zustände der Zielrechner.<br />
Ein einfaches Playbook für die Updates des Raspberrys könnte so aussehen:</p>
<h3>update_pi.yml</h3>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-json" data-lang="JSON"><code>---
- name: Update Raspberry Pi
  hosts: raspberry
  become: yes

  tasks:
    - name: Update apt cache
      apt:
        update_cache: yes

    - name: Upgrade all packages
      apt:
        upgrade: dist</code></pre>
</div>
<h3>Im Detail</h3>
<table>
<thead>
<tr>
<th>Ansible</th>
<th>Bash</th>
</tr>
</thead>
<tbody>
<tr>
<td>become: yes</td>
<td>sudo</td>
</tr>
<tr>
<td>apt: update_cache: yes</td>
<td>apt update</td>
</tr>
<tr>
<td>upgrade: dist</td>
<td>apt upgrade</td>
</tr>
<tr>
<td>autoremove: yes</td>
<td>apt autoremove</td>
</tr>
</tbody>
</table>
<h2>Ansible starten</h2>
<p>Wenn du diese drei Dateien anegelegt hast, kann es auch schon losgehen</p>
<h3>ping</h3>
<p>Der ping-Befehl ist ein Testmodul von Ansible, das überprüft, ob der Host erreichbar und ausführbar ist.<br />
Wenn du Passwort-Login benutzt musst du Ansible sagen, dass es nach einem SSH-Passwort fragen soll:<br />
Das -k steht für &#8211;ask-pass</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-bash" data-lang="Bash"><code>ansible -i hosts raspberry -m ping -k
SSH password:</code></pre>
</div>
<p>Wenn alles funktioniert, solltest du folgenden Output sehen:</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-bash" data-lang="Bash"><code>pi1 | SUCCESS =&gt; {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python3"
},
"changed": false,
"ping": "pong"
}</code></pre>
</div>
<h3>Playbook starten</h3>
<p>Jetzt zum eigentlichen Task:</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-bash" data-lang="Bash"><code>ansible-playbook -i hosts update_pi.yml -k -K</code></pre>
</div>
<p>Da wir sudu apt update / upgrade machen wollen, müssen wir auch das root-PW übergeben. Das machen wir mit dem Flag -K.<br />
-K steht für &#8211;ask-become-pass, damit wird Ansible nach dem sudo-Passwort fragen.</p>
<h2>Zertifikat-Login</h2>
<p>Da wir aber nicht permanent die<br />
Ansible unterstützt auch die Authentifizierung mit Zertifikaten, was sicherer ist als die Passwort-Authentifizierung.<br />
Dazu musst du auf deinem Control-Rechner ein SSH-Schlüsselpaar generieren und den öffentlichen Schlüssel auf den<br />
Zielrechner kopieren.</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-bash" data-lang="Bash"><code>ssh-keygen -t rsa -b 4096 -C "@ansible-controller"
ssh-copy-id @192.168.178.66</code></pre>
</div>
<p>Danach kannst du Ansible ohne Passwort verwenden, indem du einfach den Hostnamen angibst:</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-bash" data-lang="Bash"><code>ansible-playbook -i hosts update_pi.yml -K</code></pre>
</div>
<h2>Weitere Beispiele</h2>
<h3>Ubuntu-Pakete installieren</h3>
<p>Man kann natürlich auch andere Pakete installieren, z.B. git:</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-json" data-lang="JSON"><code>tasks: 
- name: Install git 
  apt: 
    name: git 
    state: present 
    update_cache: yes</code></pre>
</div>
<h3>Git Repository klonen</h3>
<p>Auch das Klonen eines Git-Repositories ist mit Ansible kein Problem:</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-json" data-lang="JSON"><code>tasks: 
- name: Clone Git repo 
    git:
      repo: https://github.com/user/meinprojekt.git 
      dest: /home/pi/meinprojekt 
      version: main</code></pre>
</div>
<h2>Fazit</h2>
<p>Ansible ist ein mächtiges Werkzeug, um die Wartung von Robotern zu automatisieren. Es ermöglicht dir, den Zustand deiner<br />
Systeme zu beschreiben und automatisch umzusetzen, ohne dich manuell auf jedem Rechner einzuloggen. Gerade in Umgebungen<br />
mit vielen verteilten Rechnern spart das enorm viel Zeit und Nerven.</p>
<p>The post <a href="https://creatronix.de/so-verwaltest-du-deine-roboter-mit-ansible/">So verwaltest du deine Roboter mit Ansible</a> appeared first on <a href="https://creatronix.de">Creatronix</a>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Alles, was Du über SCRUM wissen musst</title>
		<link>https://creatronix.de/alles-was-du-ueber-scrum-wissen-musst/</link>
		
		<dc:creator><![CDATA[Jörn]]></dc:creator>
		<pubDate>Tue, 10 Mar 2026 18:45:41 +0000</pubDate>
				<category><![CDATA[Software Engineering & Programming]]></category>
		<guid isPermaLink="false">https://creatronix.de/?p=7886</guid>

					<description><![CDATA[<p>Motivation SCRUM wurde von Ken Schwaber und Jeff Sutherland entwickelt und ist ein agiles Framework für die Softwareentwicklung. Es hilft Teams, komplexe Projekte zu managen und schnell auf Veränderungen zu reagieren. SCRUM fördert die Zusammenarbeit, Transparenz und kontinuierliche Verbesserung. Werte Commitment, Focus, Openness, Respect, and Courage Vorgehensmodell Interativ &#38; Inkrementell Entwicklungszeitraum wird in Sprints unterteilt&#8230;</p>
<p>The post <a href="https://creatronix.de/alles-was-du-ueber-scrum-wissen-musst/">Alles, was Du über SCRUM wissen musst</a> appeared first on <a href="https://creatronix.de">Creatronix</a>.</p>
]]></description>
										<content:encoded><![CDATA[<h2>Motivation</h2>
<p>SCRUM wurde von Ken Schwaber und Jeff Sutherland entwickelt und ist ein agiles Framework für die Softwareentwicklung.<br />
Es hilft Teams, komplexe Projekte zu managen und schnell auf Veränderungen zu reagieren.<br />
SCRUM fördert die Zusammenarbeit, Transparenz und kontinuierliche Verbesserung.</p>
<h2>Werte</h2>
<p>Commitment, Focus, Openness, Respect, and Courage</p>
<h2>Vorgehensmodell</h2>
<h3>Interativ &amp; Inkrementell</h3>
<p>Entwicklungszeitraum wird in Sprints unterteilt</p>
<h3>Sprints</h3>
<p>Dauer: 2-4 Wochen<br />
Ziel: funktionierende Software nach jedem Sprint mit neuen Features die den Geschäftswert des Produkte maximieren</p>
<h3>Messen der Velocity</h3>
<ul>
<li>Velocity = Anzahl der Story Points, die in einem Sprint abgeschlossen wurden</li>
<li>hilft bei der Planung zukünftiger Sprints</li>
</ul>
<h2>Rollen</h2>
<h3>Product Owner</h3>
<p>Stellvertreter des Kunden und der Stakeholder<br />
Der Product Owner ist verantwortlich für:</p>
<ul>
<li>Produktvision und Strategie</li>
<li>Product Backlog erstellen und priorisieren</li>
<li>Anforderungen verstehen, übersetzen und priorisieren</li>
<li>Wert für das Produkt maximieren</li>
<li>Stakeholder koordinieren</li>
</ul>
<p>Er ist die zentrale Entscheidungsinstanz für das Produkt.</p>
<h3>Scrum Master</h3>
<ul>
<li>coacht Teammitglieder und Organisation</li>
<li>verwaltet impediment backlog</li>
<li>löst impediments auf</li>
<li>moderiert Meetings mit dem Ziel, die Effizienz des Teams zu verbessern</li>
<li>schützt das Team vor äußeren Störungen</li>
<li>fördert die Einhaltung von Scrum-Prinzipien und -Praktiken</li>
</ul>
<h3>Scrum Team</h3>
<ul>
<li>schätzt User Stories</li>
<li>setzt Stories um</li>
<li>arbeitet selbstorganisiert</li>
<li>cross-funktional (Entwickler, Tester, Designer etc.)</li>
<li>verantwortlich für die Lieferung von Inkrementen am Ende jedes Sprints</li>
</ul>
<h2>Artefakte</h2>
<h3>Inkrement</h3>
<p>Neue Software, die am Ende eines Sprints fertiggestellt und potenziell auslieferbar ist.</p>
<h3>Product-Backlog</h3>
<ul>
<li>enthält User Stories mit Definition of Done (DoD) und Akzeptanzkriterien</li>
<li>priorisiert nach Business Value</li>
<li>dynamisch, wird während des Projekts angepasst</li>
</ul>
<h3>Sprint-Backlog</h3>
<ul>
<li>alle Items für den Sprint, inkl. DoD und Akzeptanzkriterien</li>
<li>Burndown / Burnup Chart für Fortschrittsverfolgung</li>
</ul>
<h3>Impediment-Backlog</h3>
<p>Für Hindernisse, die das Team daran hindern, die Sprint-Ziele zu erreichen<br />
Beispiele:</p>
<ul>
<li>CI Pipeline funktioniert nicht</li>
<li>Zugang zu Testdaten fehlt</li>
<li>API Dokumentation unvollständig</li>
<li>Fehlende Hardware für Tests</li>
</ul>
<h2>Termine</h2>
<h3>Backlog Refinement (früher Backlog Grooming)</h3>
<p>Zeitpunkt: 1-2 mal pro Sprint, oft in der Mitte des Sprints<br />
Dauer: 1-2 Stunden<br />
Ziel: User Stories mit DoD, Akzeptanzkriterien und Story Points versehen, damit sie für die Sprintplanung bereit sind</p>
<p><strong>Planning Poker</strong></p>
<p>Wie geschätzt wird, lässt Scrum offen, aber Planning Poker ist beliebt.<br />
Jeder Teilnehmer erhält Karten mit Zahlen (z.B. Fibonacci-Folge: 1, 2, 3, 5, 8, 13)</p>
<p>Die Zahlen stehen für die Komplexität der Aufgabe von 1 &#8211; sehr simpel bis 13 sehr komplex.<br />
Der Product Owner liest eine User Story vor, und die Teilnehmer schätzen gleichzeitig, indem sie eine Karte mit ihrer Schätzung hochhalten<br />
Nach der Schätzung diskutieren die Teilnehmer, um ein gemeinsames Verständnis zu erreichen.</p>
<h3>Sprint Planning</h3>
<p>Zeitpunkt: Am Anfang eines Sprints<br />
Dauer: 4-8 Stunden (je nach Sprintlänge)<br />
Ziel: Sprint-Ziele festlegen, User Stories auswählen, die im Sprint bearbeitet werden sollen, und Aufgaben planen</p>
<h3>Daily Standup</h3>
<p>Zeitpunkt: Täglich, meist morgens<br />
Dauer: 15 Minuten<br />
Ziel: Mini-Retro: Awareness für erledigte und aktuelle Tätigkeiten, Impediments an Scrum Master kommunizieren</p>
<h3>Sprint Retro</h3>
<p>Zeitpunkt: Am Sprint-Ende<br />
Dauer: 1-2 Stunden<br />
Ziel: Reflexion über den vergangenen Sprint, Identifikation von Verbesserungsmöglichkeiten, Maßnahmen für den nächsten Sprint planen</p>
<p>&nbsp;</p>
<h2>Scrum but &#8211; Antipatterns</h2>
<p>Wenn Scrum nicht richtig umgesetzt wird, können verschiedene Probleme auftreten, z.B.:</p>
<ul>
<li>Scrum Master ist nur ein Titel, aber keine echte Rolle im Team</li>
<li>Product Owner ist nicht verfügbar oder hat keine Entscheidungsbefugnis</li>
<li>Team arbeitet nicht selbstorganisiert, sondern wird von außen gesteuert</li>
<li>Sprints werden nicht eingehalten, sondern ständig verlängert oder verkürzt</li>
<li>Meetings werden nicht effektiv genutzt, sondern sind reine Zeitverschwendung</li>
<li>Keine klare Definition of Done, was zu unklaren Anforderungen und Qualitätsproblemen führt</li>
<li>Keine regelmäßige Retrospektive, was zu fehlender kontinuierlicher Verbesserung führt</li>
<li>Keine klare Priorisierung im Product Backlog, was zu ineffizienter Arbeit und fehlendem Fokus führt</li>
</ul>
<h2>Quellen</h2>
<p><a href="https://scrumguides.org/scrum-guide.html">https://scrumguides.org/scrum-guide.html</a></p>
<p>The post <a href="https://creatronix.de/alles-was-du-ueber-scrum-wissen-musst/">Alles, was Du über SCRUM wissen musst</a> appeared first on <a href="https://creatronix.de">Creatronix</a>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Linux Shell Kommandos Cheatsheet</title>
		<link>https://creatronix.de/linux-shell-kommandos-cheatsheet/</link>
		
		<dc:creator><![CDATA[Jörn]]></dc:creator>
		<pubDate>Wed, 04 Mar 2026 11:54:04 +0000</pubDate>
				<category><![CDATA[Software Engineering & Programming]]></category>
		<guid isPermaLink="false">https://creatronix.de/?p=7878</guid>

					<description><![CDATA[<p>Motivation In Robotik-Projekten wird in den meisten Fällen mit einer Linux-Distribution wie Ubuntu gearbeitet, deswegen ist es von Vorteil, sich auch auf der Linux-Konsole aka shell auszukennen. Die Standard-Shell ist meistens Bash (Bourne Again Shell). Viele ROS-Tools erwarten explizit Bash: source /opt/ros/humble/setup.bash Informationen über Kommandos Mit dem Kommando man kann man sich Infos zu den&#8230;</p>
<p>The post <a href="https://creatronix.de/linux-shell-kommandos-cheatsheet/">Linux Shell Kommandos Cheatsheet</a> appeared first on <a href="https://creatronix.de">Creatronix</a>.</p>
]]></description>
										<content:encoded><![CDATA[<h2>Motivation</h2>
<p>In Robotik-Projekten wird in den meisten Fällen mit einer Linux-Distribution wie Ubuntu gearbeitet, deswegen ist es von Vorteil, sich auch auf der Linux-Konsole aka shell auszukennen.</p>
<p>Die Standard-Shell ist meistens Bash (Bourne Again Shell). Viele ROS-Tools erwarten explizit Bash:</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-bash" data-lang="Bash"><code>source /opt/ros/humble/setup.bash</code></pre>
</div>
<h2>Informationen über Kommandos</h2>
<p>Mit dem Kommando man kann man sich Infos zu den Kommandos geben lassen:</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-bash" data-lang="Bash">man ls</pre>
</div>
<p>Eine moderne Alternative ist tldr:</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-bash" data-lang="Bash"><code>tldr ls</code></pre>
</div>
<p>Du kannst es unter Ubuntu mit</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-bash" data-lang="Bash"><code>sudo snap install tldr</code></pre>
</div>
<p>installieren.</p>
<h2>Datei-Handling</h2>
<table>
<thead>
<tr>
<th>Befehl</th>
<th>Bedeutung</th>
<th>Nützliche Optionen</th>
</tr>
</thead>
<tbody>
<tr>
<td>ls</td>
<td>Verzeichnisinhalt anzeigen</td>
<td>-a verstecdkte Dateien<br />
-h human readable<br />
-l detaillierte List</td>
</tr>
<tr>
<td>cd</td>
<td>Verzeichnis wechseln</td>
<td>.. eine Ebene höher / ~ home Verzeichnis</td>
</tr>
<tr>
<td>pwd</td>
<td>aktuelles Verzeichnis anzeigen</td>
<td></td>
</tr>
<tr>
<td>mkdir</td>
<td>make directory: Verzeichnis erstellen</td>
<td></td>
</tr>
<tr>
<td>touch</td>
<td>Datei erstellen / Timestamp ändern</td>
<td></td>
</tr>
<tr>
<td>rm</td>
<td>Datei / Ordner löschen</td>
<td>-r rekursiv / -f force</td>
</tr>
<tr>
<td>cp</td>
<td>Datei kopieren</td>
<td>-r rekursiv</td>
</tr>
<tr>
<td>mv</td>
<td>Datei verschieben oder umbenennen</td>
<td></td>
</tr>
<tr>
<td>ln</td>
<td>Link erstellen</td>
<td>-s symbolischer Link</td>
</tr>
<tr>
<td>du</td>
<td>Speicherverbrauch anzeigen</td>
<td>-h human readable</td>
</tr>
<tr>
<td>df</td>
<td>freien Speicher anzeigen</td>
<td>-h human readable</td>
</tr>
<tr>
<td>which</td>
<td>Pfad eines Programms anzeigen</td>
<td>-a alle Treffer</td>
</tr>
<tr>
<td>tar</td>
<td>Archive packen/entpacken</td>
<td>-xvf entpacken</td>
</tr>
<tr>
<td>find</td>
<td>Dateien suchen</td>
<td>-name, -type</td>
</tr>
<tr>
<td>grep</td>
<td>Text in Dateien suchen</td>
<td>-r rekursiv</td>
</tr>
</tbody>
</table>
<h2>Benutzer &amp; Rechte</h2>
<table>
<thead>
<tr>
<th>Befehl</th>
<th>Bedeutung</th>
<th>Nützliche Optionen</th>
</tr>
</thead>
<tbody>
<tr>
<td>whoami</td>
<td>aktuellen Benutzer anzeigen</td>
<td></td>
</tr>
<tr>
<td>id</td>
<td>Benutzer- und Gruppeninformationen</td>
<td></td>
</tr>
<tr>
<td>sudo</td>
<td>Befehl als root ausführen</td>
<td></td>
</tr>
<tr>
<td>chmod</td>
<td>Dateirechte ändern</td>
<td>chmod +x script.sh</td>
</tr>
<tr>
<td>chown</td>
<td>Eigentümer ändern</td>
<td>sudo chown user:file</td>
</tr>
</tbody>
</table>
<h2>Linux-Pakete &amp; Versionsinformationen</h2>
<table>
<thead>
<tr>
<th>Befehl</th>
<th>Bedeutung</th>
<th>Nützliche Optionen</th>
</tr>
</thead>
<tbody>
<tr>
<td>apt</td>
<td>Paketmanager</td>
<td>install <package_name>, update, upgrade</package_name></td>
</tr>
<tr>
<td>dpkg</td>
<td>lokale .deb installieren</td>
<td>dpkg -i file.deb</td>
</tr>
<tr>
<td>lsb_release -a</td>
<td>Distribution anzeigen</td>
<td></td>
</tr>
<tr>
<td>uname -a</td>
<td>Kernelinformationen</td>
<td></td>
</tr>
</tbody>
</table>
<h2>Prozesse</h2>
<table>
<thead>
<tr>
<th>Befehl</th>
<th>Bedeutung</th>
<th>Nützliche Optionen</th>
</tr>
</thead>
<tbody>
<tr>
<td>ps</td>
<td>Prozesse anzeigen</td>
<td>ps aux</td>
</tr>
<tr>
<td>top</td>
<td>Systemmonitor</td>
<td></td>
</tr>
<tr>
<td>htop</td>
<td>CPU und RAM Verbrauch anzeigen</td>
<td></td>
</tr>
<tr>
<td>kill</td>
<td>Prozess per PID beenden</td>
<td></td>
</tr>
<tr>
<td>pkill</td>
<td>Prozess per Name beenden</td>
<td></td>
</tr>
<tr>
<td>jobs</td>
<td>Hintergrundjobs anzeigenn</td>
<td></td>
</tr>
</tbody>
</table>
<h2>Netzwerke</h2>
<table>
<thead>
<tr>
<th>Befehl</th>
<th>Bedeutung</th>
<th>Nützliche Optionen</th>
</tr>
</thead>
<tbody>
<tr>
<td>ip a</td>
<td>Netzwerkschnittstellen anzeigen</td>
<td></td>
</tr>
<tr>
<td>ping</td>
<td>Verbindung testen</td>
<td>ping heise.de</td>
</tr>
<tr>
<td>ss</td>
<td>offene Ports anzeigen</td>
<td>-tulpen</td>
</tr>
<tr>
<td>curl</td>
<td>HTTP Requests</td>
<td></td>
</tr>
<tr>
<td>ssh</td>
<td>Remote Login</td>
<td></td>
</tr>
</tbody>
</table>
<h2>USB</h2>
<table>
<thead>
<tr>
<th>Befehl</th>
<th>Bedeutung</th>
</tr>
</thead>
<tbody>
<tr>
<td>lsusb</td>
<td>USB-Geräte anzeigen</td>
</tr>
<tr>
<td>dmesg</td>
<td>Kernel-Logs (z. B. beim Einstecken)</td>
</tr>
</tbody>
</table>
<p>The post <a href="https://creatronix.de/linux-shell-kommandos-cheatsheet/">Linux Shell Kommandos Cheatsheet</a> appeared first on <a href="https://creatronix.de">Creatronix</a>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Pytest Tutorial</title>
		<link>https://creatronix.de/pytest-tutorial/</link>
		
		<dc:creator><![CDATA[Jörn]]></dc:creator>
		<pubDate>Fri, 06 Feb 2026 20:00:23 +0000</pubDate>
				<category><![CDATA[Python]]></category>
		<category><![CDATA[Software Engineering & Programming]]></category>
		<guid isPermaLink="false">https://creatronix.de/?p=7846</guid>

					<description><![CDATA[<p>Motivation Tests sind ein zentraler Bestandteil moderner Entwicklung. Das Framework pytest macht das Schreiben, Organisieren und Ausführen von Tests einfach, flexibel und leistungsfähig. Tests schreiben und ausführen Installation Zuerst installierst du pytest mit: pip install pytest pytest ist kompatibel mit unittest-Tests und erfordert keine Testklassen. Namenskonventionen Damit pytest Tests automatisch findet, müssen Dateien und Funktionen&#8230;</p>
<p>The post <a href="https://creatronix.de/pytest-tutorial/">Pytest Tutorial</a> appeared first on <a href="https://creatronix.de">Creatronix</a>.</p>
]]></description>
										<content:encoded><![CDATA[<h2>Motivation</h2>
<p>Tests sind ein zentraler Bestandteil moderner Entwicklung. Das Framework pytest macht das Schreiben, Organisieren und Ausführen von Tests einfach, flexibel und leistungsfähig.</p>
<h2>Tests schreiben und ausführen</h2>
<h3>Installation</h3>
<p>Zuerst installierst du pytest mit:</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-bash" data-lang="Bash"><code>pip install pytest</code></pre>
</div>
<p>pytest ist kompatibel mit unittest-Tests und erfordert keine Testklassen.</p>
<h3>Namenskonventionen</h3>
<p>Damit pytest Tests automatisch findet, müssen Dateien und Funktionen bestimmte Namen haben:</p>
<ul>
<li>Dateinamen haben test im Namen</li>
<li>Funktionsnamen beginnen mit test_</li>
</ul>
<p>Beispiel:</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-python" data-lang="Python"><code>def test_example():
    x = 5
    assert x == 5</code></pre>
</div>
<p>Mit pytest im Terminal führt pytest alle passenden Tests aus.</p>
<h3>Testauswahl</h3>
<p>Du kannst gezielt Tests ausführen:</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-python" data-lang="Python"><code>pytest test_module.py::test_function
pytest -k "keyword"</code></pre>
</div>
<p>Optionen wie -v (verbose), -x (bei Fehler abbrechen) oder -s (print-Ausgaben anzeigen) sind sehr praktisch.</p>
<p>Um Optionen nicht immer per Hand eingeben zu müssen, können diese auch in einer pytest.ini angegeben werden:</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-plain" data-lang="Plain Text"><code>[pytest]
pythonpath = .
addopts = -x --durations=0 -vv</code></pre>
</div>
<h2>Fortgeschrittene pytest-Techniken</h2>
<h3>Fixtures</h3>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-python" data-lang="Python"><code>import pytest

@pytest.fixture(scope="function")
def simple_fixture():
    return 42

def test_function(simple_fixture):
    assert simple_fixture == 42</code></pre>
</div>
<h3>Fixture scopes</h3>
<p>pytest-Fixtures haben einen Scope, der bestimmt, wie oft eine Fixture erzeugt und wiederverwendet wird.<br />
Der Standard-Scope ist function, d. h. die Fixture wird für jeden Test neu ausgeführt.</p>
<p>Weitere Scopes sind:</p>
<ul>
<li>class &#8211; einmal pro Testklasse</li>
<li>module &#8211; einmal pro Datei</li>
<li>package &#8211; einmal pro Paket</li>
<li>session &#8211; einmal pro gesamtem Testlauf</li>
</ul>
<p>Größere Scopes verbessern die Performance, bergen aber das Risiko von geteiltem Zustand zwischen Tests.<br />
Daher gilt: so klein wie möglich, so groß wie nötig.</p>
<h3>Setup and teardown</h3>
<p>Mit pytest können wir klassische setup / teardown Methoden mit fixtures nachbauen</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-python" data-lang="Python"><code>import pytest

def test_setup_and_teardown():
    assert True

@pytest.fixture(autouse=True, scope="function")
def setup_and_teardown():
    print("SetUp")
    # setup code goes here
    yield
    print("TearDown")
    # teardown code goes here</code></pre>
</div>
<h3>Parametrisierung</h3>
<p>Mit Parametrisierung lässt sich derselbe Test mit verschiedenen Daten ausführen. So umgehst du Duplikate:</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-python" data-lang="Python"><code>import pytest

@pytest.mark.parametrize("data, expected", [
    ([1], 1),
    ([1, 2], 1.5),
    ([1, 2, 3], 2),
])
def test_calculate_mean(data, expected_mean):
    mean = calculate_mean(data)
    assert mean == expected_mean

def calculate_mean(data):
    return sum(data) / len(data)</code></pre>
</div>
<p>&nbsp;</p>
<p>Das definiert mehrere Testinstanzen automatisch.</p>
<h3>Marker</h3>
<p>Marker helfen, Gruppen von Tests zu steuern.<br />
Marker eignen sich z. B. zur Gruppierung nach Plattform, Kategorie oder Dauer.</p>
<p>Es gibt drei eingebaute Marker:</p>
<ul>
<li>skip</li>
<li>skipif</li>
<li>xfail</li>
</ul>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-python" data-lang="Python"><code>import pytest
import sys

@pytest.mark.skip(reason="Feature not yet implemented")
def test_future():
    pass

@pytest.mark.skipif(sys.platform == "win32", reason="Linux only")
def test_linux_only():
    pass

@pytest.mark.xfail(reason="Bug #123")
def test_known_bug():
    pass</code></pre>
</div>
<h3>Eigene Marker</h3>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-python" data-lang="Python"><code>import pytest

@pytest.mark.slow
def test_slow():
    pass</code></pre>
</div>
<p>In pytest.ini:</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-plain" data-lang="Plain Text"><code>[pytest]
...
markers =
    slow: mark slow tests</code></pre>
</div>
<p>Dann kannst du testen mit:</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-bash" data-lang="Bash"><code>pytest -m slow</code></pre>
</div>
<p>Oder alle nicht langsamen Tests laufen lassen:</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-bash" data-lang="Bash"><code>pytest -m "not slow"</code></pre>
</div>
<h3>Nahezu gleiche Vergleiche</h3>
<p>Für Fließkommazahlen ist ein exakter Vergleich oft ungeeignet. pytest bietet das approx-Feature:</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-python" data-lang="Python"><code>assert 2.2 == pytest.approx(2.3, 0.1)</code></pre>
</div>
<p>Erlaubt eine Toleranz statt exakter Übereinstimmung.</p>
<h3>Exceptions testen</h3>
<p>Mit pytest kannst du das Auftreten von Ausnahmen elegant testen:</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-python" data-lang="Python"><code>def test_zero_division():
    with pytest.raises(ZeroDivisionError):
        1 / 0</code></pre>
</div>
<p>Das prüft, ob ein Fehler erwartet korrekt geworfen wird.</p>
<h2>Test-Reporting &amp; Coverage</h2>
<p>Testabdeckung mit pytest-cov</p>
<p>Mit dem Plugin pytest-cov lässt sich messen, wie viel deines Codes getestet wird:</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-bash" data-lang="Bash"><code>pip install pytest-cov

pytest --cov=my_app 10_app_test</code></pre>
</div>
<p>Ergebnis: Coverage-Statistiken zeigen an, wie viele Zeilen getestet wurden.</p>
<h3>HTML-Reports</h3>
<p>Du kannst mit demselben Plugin HTML-Berichte erzeugen:</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-bash" data-lang="Bash"><code>pytest --cov=my_app --cov-report=html</code></pre>
</div>
<p>Eine visuelle Übersicht über die Abdeckung hilft bei der Analyse.</p>
<h3>Integration mit Werkzeugen</h3>
<p>Coverage-Daten lassen sich in Tools wie SonarQube einbinden:</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-bash" data-lang="Bash"><code>pytest --cov-report=xml:coverage.xml</code></pre>
</div>
<p>Dadurch kannst du Metriken in CI/CD-Pipelines oder Dashboards verwenden.</p>
<h2>Assertions mit Hamcrest (optional, aber mächtig)</h2>
<p>Neben den eingebauten assert-Anweisungen von pytest kann man auch Hamcrest verwenden.<br />
Hamcrest ist eine Matcher-Bibliothek, die Tests lesbarer und ausdrucksstärker machen kann – besonders bei komplexen Datenstrukturen.</p>
<h3>Installation</h3>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-bash" data-lang="Bash"><code>pip install pyhamcrest</code></pre>
</div>
<p>Import in Tests:</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-python" data-lang="Python"><code>from hamcrest import assert_that, equal_to</code></pre>
</div>
<h3>Warum Hamcrest?</h3>
<p>Vergleich:</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-python" data-lang="Python"><code>def test_hamcrest_assert():
    result = 42
    assert_that(result, equal_to(42))</code></pre>
</div>
<p>Der Vorteil zeigt sich bei komplexeren Assertions:</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-python" data-lang="Python"><code>def test_hamcrest_start_with():
    name = "Max Mustermann"
    assert_that(name, starts_with("Max"))

def test_hamcrest_has_length():
    items: Sized = ['apple', 'banana', 'cherry']
    assert_that(items, has_length(3))</code></pre>
</div>
<p>Tests lesen sich mehr wie Spezifikationen.</p>
<h3>Listen &amp; Collections</h3>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-python" data-lang="Python"><code>from hamcrest import assert_that, has_items
from collections.abc import Sequence

def test_compare_list_of_items():
    actual_list_of_items: Sequence[str] = ['apple', 'banana', 'cherry']
    assert_that(actual_list_of_items, has_items("apple", "banana"))</code></pre>
</div>
<h2>Fazit</h2>
<p>pytest ist ein einfaches, doch mächtiges Framework zur Testautomatisierung in Python. Es kombiniert:</p>
<ul>
<li>geringe Einstiegshürde – einfache Tests schnell geschrieben</li>
<li>hohe Flexibilität – Marker, Parameter, Coverage</li>
<li>gute Tools-Integration – HTML-Reports oder CI-Coverage</li>
</ul>
<p>Damit eignet es sich sowohl für Einsteiger als auch für professionelle Test-Pipelines.</p>
<h2>Code</h2>
<p><a href="https://github.com/jboegeholz/pytest_tutorial">https://github.com/jboegeholz/pytest_tutorial</a></p>
<p>The post <a href="https://creatronix.de/pytest-tutorial/">Pytest Tutorial</a> appeared first on <a href="https://creatronix.de">Creatronix</a>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>So verwendest Du den std::vector &#8211; Modernes C++</title>
		<link>https://creatronix.de/std-vector-modern-cpp/</link>
		
		<dc:creator><![CDATA[Jörn]]></dc:creator>
		<pubDate>Tue, 06 Jan 2026 15:18:25 +0000</pubDate>
				<category><![CDATA[C++]]></category>
		<guid isPermaLink="false">https://creatronix.de/?p=7817</guid>

					<description><![CDATA[<p>Motivation Auf meiner Lernreise in die Welt der Robotik ist eines meiner Ziele, mein angestaubtes C++-Wissen aufzubessern. Heute schauen wir uns den std::vector an. Der std::vector ist ein praktisches Hilfsmittel, um in C++ Daten zu verwalten. Man kann ihn mit der Liste in Python vergleichen. Im Gegensatz zum std::array ist der std::vector eine dynamische Datenstruktur,&#8230;</p>
<p>The post <a href="https://creatronix.de/std-vector-modern-cpp/">So verwendest Du den std::vector &#8211; Modernes C++</a> appeared first on <a href="https://creatronix.de">Creatronix</a>.</p>
]]></description>
										<content:encoded><![CDATA[<h2>Motivation</h2>
<p>Auf meiner Lernreise in die Welt der Robotik ist eines meiner Ziele, mein angestaubtes C++-Wissen aufzubessern.<br />
Heute schauen wir uns den std::vector an. Der std::vector ist ein praktisches Hilfsmittel, um in C++ Daten zu verwalten.<br />
Man kann ihn mit der Liste in Python vergleichen. Im Gegensatz zum std::array ist der std::vector eine dynamische Datenstruktur, die wächst, wenn neue Werte hinzugefügt werden und die bisherige Kapazität nicht mehr ausreicht.</p>
<h2>Historie</h2>
<p>Den std::vector gibt es bereits ab C++98, er wurde von <a href="https://de.wikipedia.org/wiki/Alexander_Alexandrowitsch_Stepanow_(Informatiker)">Alexander Stepanov</a> entwickelt. Seit C++11 sind einige interessante Neuerungen hinzugekommen.</p>
<h2>Include</h2>
<p>Um einen Vector zu benutzen, wird der Header vector eingebunden:</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-cpp" data-lang="C++"><code>#include &lt;vector&gt;<vector>
</vector></code></pre>
</div>
<h2>Deklaration</h2>
<p>Der std::vector ist eine Template-Datenstruktur, d.h. wir müssen ihn mit dem Datentyp parametrieren, der eingefügt werden soll:</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-cpp" data-lang="C++"><code>TEST(VectorTest, VectorInitialSize) {
    const std::vector<int> int_vector;
    EXPECT_EQ(int_vector.capacity(), 0);
    EXPECT_EQ(int_vector.size(), 0);
    EXPECT_EQ(int_vector.empty(), true);
}
</int></code></pre>
</div>
<p>Die Kapazität und die Größe sind am Anfang 0; empty() gibt true zurück</p>
<h2>Daten einfügen</h2>
<p>Die einfachste Art, Daten in den Vector zu packen, ist die push_back()-Methode. Sie hängt ein neues Element an das Ende des vectors</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-cpp" data-lang="C++"><code>TEST(VectorTest, VectorPushBack) {
    std::vector<int> int_vector;
    int_vector.push_back(1);
    int_vector.push_back(2);
    int_vector.push_back(3);
    EXPECT_EQ(int_vector.size(), 3);
}
</int></code></pre>
</div>
<p>Mit der Insert-Methode kann an einer beliebigen Stelle im Vector ein Element eingefügt werden:</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-cpp" data-lang="C++"><code>TEST(VectorTest, VectorElementInsert) {
    std::vector<int> int_vector = {1,2,3};
    int_vector.insert(int_vector.begin(), 0);
    EXPECT_EQ(int_vector.at(0), 0);
}
</int></code></pre>
</div>
<p>Allerdings werden dann Elemente um jeweils eine Stelle verschoben (kopiert) was zur Laufzeit in O(n) führt, also O(n²).<br />
Bei vielen Elementen kann es sich lohnen push_back + std::sort zu verwenden, das ist dann in O(n log n)<br />
Zu beachten ist hier zudem, dass die Stelle an der eingefügt werden soll, nicht durch einen Index, sondern durch einen Iterator angegeben wird.</p>
<h2>Daten auslesen</h2>
<p>Grundsätzlich unterstützt vector sowohl den Index-Operator als auch die at()-Methode:</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-cpp" data-lang="C++"><code>TEST(VectorTest, VectorGetElement) {
    const std::vector<int> int_vector = {1,2,3,4,5};
    EXPECT_EQ(int_vector[2], 3);
    EXPECT_EQ(int_vector.at(2), 3);
}
</int></code></pre>
</div>
<p>Die at()-Methode ist zu bevorzugen, weil sie eine Exception wirft, wenn der Index nicht verfügbar ist.</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-cpp" data-lang="C++"><code>
TEST(VectorTest, VectorGetElementException) {
    const std::vector<int> int_vector = {1,2,3,4,5};
    EXPECT_THROW((void)int_vector.at(10), std::out_of_range);
}
</int></code></pre>
</div>
<p>Wenn man am ersten Element im Vektor interessiert ist, kann die front()-Methode benutzt werden. Das letzte Element kann mit back() abgefragt werden.<br />
Aber Vorsicht: auch hier solltest du prüfen, ob der vector nicht leer ist.</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-cpp" data-lang="C++"><code>TEST(VectorTest, VectorElementBackFront) {
    const std::vector<int> int_vector = {1,2,3};
    if (!int_vector.empty())
    {
        EXPECT_EQ(int_vector.front(), 1);
        EXPECT_EQ(int_vector.back(), 3);
    }
}
</int></code></pre>
</div>
<h2>Daten löschen</h2>
<p>Mit der Methode pop_back() löschst du das letzte Element aus einem Vektor.</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-cpp" data-lang="C++"><code>
TEST(VectorTest, VectorElementPopBack) {
    std::vector int_vector = {1,2,3};
    int_vector.pop_back();
    EXPECT_EQ(int_vector.size(), 2);
}</code></pre>
</div>
<p><int>Mit der Methode clear() kann der komplette Vektor geleert werden. Die Size ist danach auch wieder = bzw. empty wieder true</int><int></int></p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-cpp" data-lang="C++"><code>TEST(VectorTest, VectorClear) {
    std::vector int_vector = {1,2,3};
    int_vector.clear();
    EXPECT_EQ(int_vector.size(), 0);
}</code></pre>
</div>
<p>&nbsp;</p>
<h2>Kapazität vs Größe</h2>
<p>Die Kapazität ist die maximal mögliche Anzahl von Elementen, die Größe die wirkliche Anzahl.<br />
Je nach Compiler wird der Vector unterschiedlich vergrößert:<br />
Clang und der GCC verdoppeln die Größe eines Vectors, sobald die Kapazität nicht mehr ausreicht, um ein weiteres Element einzufügen.</p>
<p><img decoding="async" src="https://creatronix.de/wp-content/uploads/2026/01/vector-capacity-gcc.png" alt="" width="511" height="319" class="alignnone size-full wp-image-7815" srcset="https://creatronix.de/wp-content/uploads/2026/01/vector-capacity-gcc.png 511w, https://creatronix.de/wp-content/uploads/2026/01/vector-capacity-gcc-300x187.png 300w" sizes="(max-width: 511px) 100vw, 511px" /><br />
MSVC erhöht die Kapazität um lediglich 50 %.</p>
<p><img decoding="async" src="https://creatronix.de/wp-content/uploads/2026/01/vector-capacity-msvc.png" alt="" width="423" height="362" class="alignnone size-full wp-image-7814" srcset="https://creatronix.de/wp-content/uploads/2026/01/vector-capacity-msvc.png 423w, https://creatronix.de/wp-content/uploads/2026/01/vector-capacity-msvc-300x257.png 300w" sizes="(max-width: 423px) 100vw, 423px" /></p>
<p>Das Verhalten kann mit folgendem Code überprüft werden:</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-cpp" data-lang="C++"><code>TEST(VectorTest, VectorPushBack) {
    std::vector<int> int_vector;
    int_vector.push_back(1);
    int_vector.push_back(2);
    int_vector.push_back(3);
    EXPECT_EQ(int_vector.size(), 3);

#if defined(_MSC_VER)
    EXPECT_EQ(int_vector.capacity(), 3);
#elif defined(__clang__) || defined(__GNUC__)
    EXPECT_EQ(int_vector.capacity(), 4);
#endif

    int_vector.push_back(4);
    EXPECT_EQ(int_vector.size(), 4);
    EXPECT_EQ(int_vector.capacity(), 4);

    int_vector.push_back(5);
    EXPECT_EQ(int_vector.size(), 5);

#if defined(_MSC_VER)
    EXPECT_EQ(int_vector.capacity(), 6);
#elif defined(__clang__) || defined(__GNUC__)
    EXPECT_EQ(int_vector.capacity(), 8);
#endif

}
</int></code></pre>
</div>
<p>Beide Strategien haben Vor- und Nachteile</p>
<p>Wenn du lieber von vornherein eine explizite Größe vorgeben möchtest, kannst du die reserve()-Funktion benutzen. Dann wird im Vector für die konkrete Kapazität Speicher allokiert.</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-cpp" data-lang="C++"><code>TEST(VectorTest, VectorReserve) {
    std::vector int_vector;
    int_vector.reserve(100);
    EXPECT_EQ(int_vector.capacity(), 100);
}</code></pre>
</div>
<h2>emplace_back</h2>
<p>Jetzt wird es spannend. Wir haben bisher das push_back auf einfache Datentypen angewendet.<br />
Was passiert aber, wenn wir Objekte in den Vector pushen?</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-cpp" data-lang="C++"><code>std::vector<std::string> str_vector;
str_vector.push_back("Hello world");
</std::string></code></pre>
</div>
<p>Hier wird der string erst erzeugt und dann nochmal kopiert. Das ist natürlich ineffizient, weil das initiale Objekt außerhalb des Funktions-Aufrufes nicht verwendet wird. Deshalb sparen wir uns seit C++11 die Kopie, indem wir emplace_back() verwenden.</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-cpp" data-lang="C++"><code>TEST(VectorTest, VectorEmplaceBackString) {
    std::vector<std::string> my_vector;
    my_vector.emplace_back("Hello");
    my_vector.emplace_back("World");
    EXPECT_EQ(my_vector.at(0), "Hello");
}
</std::string></code></pre>
</div>
<h2>Quellcode</h2>
<p><a href="https://github.com/jboegeholz/modern_cpp/blob/master/tests/04_test_vector.cpp">https://github.com/jboegeholz/modern_cpp/blob/master/tests/04_test_vector.cpp</a></p>
<h2>Further Reading</h2>
<p>Electronic Arts hat ihre STL-Implementierung geopensourct<br />
<a href="https://github.com/electronicarts/EASTL/blob/master/include/EASTL/vector.h">https://github.com/electronicarts/EASTL/blob/master/include/EASTL/vector.h</a></p>
<p>The post <a href="https://creatronix.de/std-vector-modern-cpp/">So verwendest Du den std::vector &#8211; Modernes C++</a> appeared first on <a href="https://creatronix.de">Creatronix</a>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>So verwendest du std::array &#8211; Modernes C++</title>
		<link>https://creatronix.de/std-array-modern-c-plus-plus/</link>
		
		<dc:creator><![CDATA[Jörn]]></dc:creator>
		<pubDate>Wed, 31 Dec 2025 13:30:29 +0000</pubDate>
				<category><![CDATA[C++]]></category>
		<guid isPermaLink="false">https://creatronix.de/?p=7777</guid>

					<description><![CDATA[<p>Motivation Auf meiner Lernreise in die Welt der Robotik ist eines meiner Ziele, mein angestaubtes C++-Wissen aufzubessern. Heute schauen wir uns das Konzept von std::array an. Wie war es früher? Vor C++11 musste man C-Style-Arrays benutzen: TEST(ArrayTest, CStyleArray) { int my_int_array[3] = {1, 2, 3}; EXPECT_EQ(my_int_array[0], 1); EXPECT_EQ(my_int_array[1], 2); EXPECT_EQ(my_int_array[2], 3); } Das hat natürlich&#8230;</p>
<p>The post <a href="https://creatronix.de/std-array-modern-c-plus-plus/">So verwendest du std::array &#8211; Modernes C++</a> appeared first on <a href="https://creatronix.de">Creatronix</a>.</p>
]]></description>
										<content:encoded><![CDATA[<h2>Motivation</h2>
<p>Auf meiner Lernreise in die Welt der Robotik ist eines meiner Ziele, mein angestaubtes C++-Wissen aufzubessern.<br />
Heute schauen wir uns das Konzept von std::array an.</p>
<h2>Wie war es früher?</h2>
<p>Vor C++11 musste man C-Style-Arrays benutzen:</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-cpp" data-lang="C++"><code>TEST(ArrayTest, CStyleArray) {
    int my_int_array[3] = {1, 2, 3};
    EXPECT_EQ(my_int_array[0], 1);
    EXPECT_EQ(my_int_array[1], 2);
    EXPECT_EQ(my_int_array[2], 3);
}
</code></pre>
</div>
<p>Das hat natürlich ein paar Nachteile:</p>
<h3>Sizeof kompliziert</h3>
<p>Die Funktion sizeof() kann eigentlich nicht direkt für Arrays verwendet werden, da sie die Größe einer Datenstruktur in Bytes angibt.<br />
Das können wir in folgendem Beispiel sehen. Ein Int-Array der Größe drei ist 12 Byte groß ein.<br />
Ein Double-Array der Größe drei hat 24 Byte.</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-cpp" data-lang="C++"><code>TEST(ArrayTest, CStyleArraySize) {
    int my_int_array[3];
    double my_double_array[3];
    EXPECT_EQ(sizeof(my_int_array), 12);
    EXPECT_EQ(sizeof(my_double_array), 24);
}
</code></pre>
</div>
<p>Deswegen musste man das Ergebnis von sizeof des Arrays noch mal durch die Größe des Datentyp teilen.</p>
<h3>Decaying</h3>
<p>Pointer Decay ist ein weiteres Problem von C-Style-Arrays.</p>
<p>In diesem Beispiel sehen wir, dass ein paar Zeiger an eine Funktion übergeben ist, Array nur noch durch den Zeiger repräsentiert wird.<br />
Sizeof von diesem Datentyp ist normalerweise acht, weil es sich um einen 64 Bit Zeiger handelt.</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-cpp" data-lang="C++"><code>int get_size(const int *arr)
{
    return sizeof(arr);
}
TEST(ArrayTest, CStyleArrayPointerDecay) {
    int my_int_array[3];
    EXPECT_EQ(sizeof(my_int_array), 12);
    EXPECT_EQ(get_size(my_int_array), 8); // decays to 64bit pointer
}</code></pre>
</div>
<h3>Unsicherer Indexzugriff</h3>
<p>Auch der Zugriff auf die Daten wie Index Operator ist nicht ungefährlich, weil nicht überprüft wird, ob es sich um einen gültigen Index handelt.</p>
<p>D.h. es fliegt keine Exception sondern das Programm stürzt normalerweise ab.<br />
Wenn man folgenden Code ausführt:</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-cpp" data-lang="C++"><code>TEST(ArrayTest, CStyleArrayIndex) {
    int my_int_array[3] = {1, 2, 3};
    my_int_array[4] = 1;
    EXPECT_EQ(my_int_array[4], 1);
}
</code></pre>
</div>
<p>Bekommt man:</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-bash" data-lang="Bash"><code>Process finished with exit code 134 (interrupted by signal 6:SIGABRT)</code></pre>
</div>
<h2>std::array &#8211; modernes C++</h2>
<p>Jetzt wollen wir uns mal anschauen, wie das std::array funktioniert.</p>
<p>Seit C++11 kann man den header &lt;array&gt; <array>inkludieren und bekommt die Möglichkeit, ein array zu definieren,<br />
dass dem C-Style-Array recht ähnlich sieht. Auch der Zugriff auf die Elemente mit dem Index Operator ist identisch:</array></p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-cpp" data-lang="C++"><code>TEST(ArrayTest, StdArray) {
    std::array&lt;int, 3&gt; my_std_array = {1, 2, 3};

    EXPECT_EQ(my_std_array[0], 1);
    EXPECT_EQ(my_std_array[1], 2);
    EXPECT_EQ(my_std_array[2], 3);

}
</code></pre>
</div>
<h2>Was macht std::array besser?</h2>
<h3>size()-Funktion</h3>
<p>std::array besitzt eine size()-Methode, die die korrekte Größe des Arrays ausgibt:</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-cpp" data-lang="C++"><code>TEST(ArrayTest, StdArraySize) {
    std::array&lt;int, 3&gt; my_int_array{};
    std::array&lt;int64_t, 3&gt; my_int_64_array{};
    EXPECT_EQ(my_int_array.size(), 3);
    EXPECT_EQ(my_int_64_array.size(), 3);
}
</code></pre>
</div>
<h3>at()-Funktion</h3>
<p>Die at()-Methode greift wie der Index-Operator auf das n-te Element zu:</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-cpp" data-lang="C++"><code>TEST(ArrayTest, StdArrayAt) {
    std::array&lt;int, 3&gt; my_std_array = {1, 2, 3};

    EXPECT_EQ(my_std_array.at(0), 1);
    EXPECT_EQ(my_std_array.at(1), 2);
    EXPECT_EQ(my_std_array.at(2), 3);
}
</code></pre>
</div>
<p>Auch das Setzen der Elemente geht über die at()-Methode:</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-cpp" data-lang="C++"><code>TEST(ArrayTest, AddElementsToArrayWithAt) {
    std::array&lt;int, 3&gt; my_array{};
    my_array.at(0) = 1;
    my_array.at(1) = 2;
    my_array.at(2) = 3;
    EXPECT_EQ(my_array[0], 1);
    EXPECT_EQ(my_array[1], 2);
    EXPECT_EQ(my_array[2], 3);
}
</code></pre>
</div>
<p>Gut ist, dass sie eine Exception wirft, wenn der Zugriff außerhalb des Arrays erfolgt:<br />
Die Exception kann dann über ein try/catch zur Laufzeit gefangen werden.</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-cpp" data-lang="C++"><code>TEST(ArrayTest, ArrayTestCatchException) {
    std::array&lt;int, 4&gt; my_std_array = {8, 7, 6, 5};

    try
    {
        std::cout &lt;&lt; "Element at postion 4: " &lt;&lt; my_std_array.at(4) &lt;&lt; std::endl;
    }
    catch (const std::out_of_range&amp; ex)
    {
        std::cerr &lt;&lt; ex.what() &lt;&lt; '\n';
        EXPECT_NE(ex.what(), nullptr);
    }
}
</code></pre>
</div>
<h3>Arrays als Funktionsparameter</h3>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-cpp" data-lang="C++"><code>void power2(std::span<int> arr)
{
    for (int &amp; i : arr) {
        i *= 2;
    }
}
TEST(ArrayTest, StdArrayFunctionCall) {
    std::array&lt;int, 4&gt; my_std_array = {8, 7, 6, 5};
    power2(my_std_array);
    EXPECT_EQ(my_std_array[0], 16);
}
</int></code></pre>
</div>
<h3>sort</h3>
<p>Da sich ein std::array wie ein normaler STL-Container verhält, lässt sich das array mit std::sort sortieren:</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-cpp" data-lang="C++"><code>TEST(ArrayTest, StdArraySort) {
    std::array&lt;int, 4&gt; my_std_array = {8, 7, 6, 5};
    std::sort(my_std_array.begin(), my_std_array.end());
    EXPECT_EQ(my_std_array[0], 5);
}
</code></pre>
</div>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-cpp" data-lang="C++"><code>TEST(ArrayTest, StdArraySortWithRanges) {
    std::array&lt;int, 4&gt; my_std_array = {8, 7, 6, 5};
    std::ranges::sort(my_std_array);
    EXPECT_EQ(my_std_array[0], 5);
}
</code></pre>
</div>
<h2>Beispiel aus der Robotik</h2>
<p><a href="https://github.com/jboegeholz/udemy_ros2_for_beginners/blob/3c082c605043155b7f617471a50bc3dbf3fa9929/src/my_cpp_pkg/src/led_panel_server.cpp#L47">https://github.com/jboegeholz/udemy_ros2_for_beginners/blob/3c082c605043155b7f617471a50bc3dbf3fa9929/src/my_cpp_pkg/src/led_panel_server.cpp#L47</a></p>
<h2>Kompletter Code</h2>
<p><a href="https://github.com/jboegeholz/modern_cpp/blob/master/tests/02_test_std_array.cpp">https://github.com/jboegeholz/modern_cpp/blob/master/tests/02_test_std_array.cpp</a></p>
<p>The post <a href="https://creatronix.de/std-array-modern-c-plus-plus/">So verwendest du std::array &#8211; Modernes C++</a> appeared first on <a href="https://creatronix.de">Creatronix</a>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>So verwendest du Google Test in deinem C++-Projekt</title>
		<link>https://creatronix.de/so-verwendest-du-google-test-in-deinem-cpp-projekt/</link>
		
		<dc:creator><![CDATA[Jörn]]></dc:creator>
		<pubDate>Sun, 28 Dec 2025 14:31:42 +0000</pubDate>
				<category><![CDATA[C++]]></category>
		<category><![CDATA[Software Engineering & Programming]]></category>
		<guid isPermaLink="false">https://creatronix.de/?p=7766</guid>

					<description><![CDATA[<p>Motivation So, auf geht&#8217;s! Test Driven Development für C++ Projekte. Google Test (oft auch als gtest abgekürzt) ist ein Test Framework, das von Google entwickelt wurde. 2008 wurde es als Open Source veröffentlicht, seit 2015 findest du es auf github. Voraussetzungen Um Google Test in deinen Projekten verwenden zu können, musst du CMake installiert haben.&#8230;</p>
<p>The post <a href="https://creatronix.de/so-verwendest-du-google-test-in-deinem-cpp-projekt/">So verwendest du Google Test in deinem C++-Projekt</a> appeared first on <a href="https://creatronix.de">Creatronix</a>.</p>
]]></description>
										<content:encoded><![CDATA[<h2>Motivation</h2>
<p>So, auf geht&#8217;s! Test Driven Development für C++ Projekte.</p>
<p>Google Test (oft auch als gtest abgekürzt) ist ein Test Framework, das von Google entwickelt wurde. 2008 wurde es als Open Source veröffentlicht, seit 2015 findest du es auf github.</p>
<h2>Voraussetzungen</h2>
<p>Um Google Test in deinen Projekten verwenden zu können, musst du CMake installiert haben. Zudem empfiehlt es sich, dein Projekt mit git zu versionieren, weil du dann Google Test als git Submodul installieren kannst.</p>
<h2>Installation</h2>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-bash" data-lang="Bash"><code>cd my_project
git submodule add https://github.com/google/googletest.git external/googletest
git submodule update --init --recursive</code></pre>
</div>
<h2>CMake</h2>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-cpp" data-lang="CMake"><code>cmake_minimum_required(VERSION 3.14)
project(my_project)

set(CMAKE_CXX_STANDARD 17)

add_subdirectory(external/googletest)
include(GoogleTest)
enable_testing()

add_executable(google_test_examples tests/00_google_test.cpp)
target_link_libraries(google_test_examples gtest_main)
gtest_discover_tests(google_test_examples)</code></pre>
</div>
<h2>Code</h2>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-cpp" data-lang="C++"><code>#include &lt;gtest/gtest.h&gt;
</code></pre>
</div>
<h3>Standard Vergleiche</h3>
<p>Standardmäßig vergleicht man gerne auf Gleichheit beziehungsweise Ungleichheit.</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-cpp" data-lang="C++"><code>TEST(GoogleTestExamples, EXPECT_EQ) {
    const int a = 10;
    const int b = 10;
    EXPECT_EQ(a, b);
}

TEST(GoogleTestExamples, EXPECT_NE) {
    const int a = 10;
    const int b = 11;
    EXPECT_NE(a, b);
}
</code></pre>
</div>
<p>Auch bool&#8217;sche Ausdrücke lassen sich vergleichen, auf true oder false. Das wäre aber auch schon mit dem eingebauten Assert direkt in C++ möglich. Deswegen schauen wir uns spannendere Testfälle an:</p>
<h3>Float und double vergleichen</h3>
<div>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-cpp" data-lang="C++"><code>TEST(GoogleTestExamples, EXPECT_FLOAT_EQ) {
    const float a = 10.0;
    const float b = 10.000001;
    EXPECT_FLOAT_EQ(a, b);
}</code></pre>
</div>
</div>
<div>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-cpp" data-lang="C++"><code>TEST(GoogleTestExamples, EXPECT_DOUBLE_EQ) {
    const float a = 10.0;
    const float b = 10.0000001;
    EXPECT_DOUBLE_EQ(a, b);
}</code></pre>
</div>
</div>
<h3>Exceptions testen</h3>
<p>Schön ist es, dass Google Test auch das Testen von Exceptions unterstützt:</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-cpp" data-lang="C++"><code>TEST(GoogleTestExamples, ArrayException) {
    constexpr std::array&lt;int, 3&gt; my_array = {1,2,3};
    EXPECT_THROW((void)my_array.at(10), std::out_of_range);
}
</code></pre>
</div>
<h2>SIGABRT testen</h2>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-cpp" data-lang="C++"><code>void double_unique_ptr() {
    auto * p = new int(10);
    const std::unique_ptr&lt;int&gt; ptr1 {p};
    const std::unique_ptr&lt;int&gt; ptr2 {p};
}
TEST(MyUniquePointer, DoubleUniquePointer) {
    EXPECT_DEATH(double_unique_ptr(), ".*");
}</code></pre>
</div>
<h2>std::out abfangen</h2>
<p>Wenn wir Code schreiben, der auf std::out schreibt, können wir mit <code>CaptureStdout</code>die Ausgabe abfangen:</p>
<div>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-cpp" data-lang="C++"><code>#include &lt;span&gt;
#include &lt;iostream&gt;
#include &lt;gtest/gtest.h&gt;

void print(const std::span&lt;const int&gt; values) {
    for (const int v : values) {
        std::cout &lt;&lt; v &lt;&lt; " ";
    }
}
TEST(SpanTest, StdPrintExtrapolation) {
    testing::internal::CaptureStdout();

    const int arr[] = {1, 2, 3, 4, 5};
    print(arr);

    const std::string output = testing::internal::GetCapturedStdout();
    EXPECT_EQ(output, "1 2 3 4 5 ");
}</code></pre>
</div>
</div>
<h2>Test Fixtures</h2>
<p>Wenn man schon etwas Erfahrung mit dem Erstellen von Tests hat, genießt man Features wie Test Fixtures.</p>
<p>Ein Test Fixture ist eine Methode, wie man für eine Vielzahl von Tests die Startbedingungen festlegen kann. Variablen initialisieren und Objekte instantiieren beispielsweise.</p>
<p>In klassischen Testframeworks wie JUnit oder auch Python Unittest muss man dafür die Setup-Methode der Testklasse verwenden. In modernen Testframeworks wie pytest, sind fixtures freistehende Funktionen.</p>
<p>In Google Test besteht ein Fixture aus einer Klasse die von ::testing::Test erbt und die SetUp()-Methode überschreibt.</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-cpp" data-lang="C++"><code>class TestFixture : public ::testing::Test {
protected:
    void SetUp() override {
        speed = 100;
    }

    int speed{};
};
</code></pre>
</div>
<p>Dann können wir mit dem Test-Makro TEST_F das Fixture verwenden:</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-cpp" data-lang="C++"><code>TEST_F(TestFixture, IncreasesSpeed) {
    speed += 50;
    EXPECT_EQ(speed, 150);
}

TEST_F(TestFixture, DecreasesSpeed) {
    speed -= 30;
    EXPECT_EQ(speed, 70);
}
</code></pre>
</div>
<h2>Parametrisierte Tests</h2>
<p>Parametrisierung Tests sind ein sehr wertvolles Werkzeug, gerade wenn man TDD benutzt, um seine Programme zu schreiben.<br />
Bei the Rule of three führt man normalerweise eine Optimierung durch, sobald man dreimal das Gleiche getan hat.<br />
Oft ist das der Aufruf einer neuen Funktion mit einem dritten Parameter.</p>
<p>Deshalb könnte dann hier ein parametrisierter Test drei Testfälle ersetzen.</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-cpp" data-lang="C++"><code>class AbsTest : public ::testing::TestWithParam&lt;std::pair&lt;int, int&gt;&gt; {
    // no setup needed
};

TEST_P(AbsTest, ComputesAbsoluteValue) {
    int input = GetParam().first;
    int expected = GetParam().second;

    EXPECT_EQ(std::abs(input), expected);
}

INSTANTIATE_TEST_SUITE_P(
    AbsValues,
    AbsTest,
    ::testing::Values(
        std::make_pair(-3, 3),
        std::make_pair(-1, 1),
        std::make_pair(0, 0),
        std::make_pair(2, 2)
    )
);
</code></pre>
</div>
<h2>Tests ausführen</h2>
<p>Um die Tests auszuführen, wechseln wir in das Build-Verzeichnis und starten die Tests über ctest:</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-bash" data-lang="Bash"><code>cd cmake-build-debug
ctest</code></pre>
</div>
<h2>Test Reports erstellen</h2>
<p>In größeren Projekten wird gerne für die Weiterverarbeitung ein Report in HTML oder XML erwartet,<br />
der sich zum Beispiel für den Import in ein Quality Gate Tool wie Sonar Cube eignet.<br />
In Google Test erreicht man dies, in dem man ctest so ausführt:</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-bash" data-lang="Bash"><code>ctest --output-junit test-report.xml</code></pre>
</div>
<h2>Übersicht Test Makros</h2>
<table>
<tbody>
<tr>
<td>Assertion</td>
<td>Bedeutung</td>
</tr>
<tr>
<td>EXPECT_EQ(a, b)</td>
<td>a == b</td>
</tr>
<tr>
<td>EXPECT_NE(a, b)</td>
<td>a != b</td>
</tr>
<tr>
<td>EXPECT_LT(a, b)</td>
<td>a &lt; b</td>
</tr>
<tr>
<td>EXPECT_LE(a, b)</td>
<td>a &lt;= b</td>
</tr>
<tr>
<td>EXPECT_GT(a, b)</td>
<td>a &gt; b</td>
</tr>
<tr>
<td>EXPECT_GE(a, b)</td>
<td>a &gt;= b</td>
</tr>
<tr>
<td>EXPECT_TRUE(expr)</td>
<td>Ausdruck ist **true**</td>
</tr>
<tr>
<td>EXPECT_FALSE(expr)</td>
<td>Ausdruck ist **false**</td>
</tr>
<tr>
<td>EXPECT_STREQ(a, b)</td>
<td>zwei const char* sind gleich</td>
</tr>
<tr>
<td>EXPECT_STRNE(a, b)</td>
<td>zwei const char* sind ungleich</td>
</tr>
<tr>
<td>EXPECT_FLOAT_EQ(a, b)</td>
<td>zwei float sind „nahe genug“</td>
</tr>
<tr>
<td>EXPECT_DOUBLE_EQ(a, b)</td>
<td>zwei double sind „nahe genug“</td>
</tr>
</tbody>
</table>
<h2>Fazit</h2>
<p>Google Test ist ein angenehmes Test-Framework mit einer entspannten Lernkurve.<br />
Die Integration in moderne IDEs wie Jetbrains CLion ist ein weiterer Pluspunkt.<br />
Den gesamten Code findest du hier:<a href="https://github.com/jboegeholz/modern_cpp/blob/master/tests/00_google_test.cpp"> https://github.com/jboegeholz/modern_cpp/blob/master/tests/00_google_test.cpp</a></p>
<h2>Weiterführende Links</h2>
<p><a href="https://creatronix.de/software-testing-concepts/">https://creatronix.de/software-testing-concepts/</a><br />
<a href="https://creatronix.de/wie-tdd-die-welt-rettet-und-deine-nerven-schont/">https://creatronix.de/wie-tdd-die-welt-rettet-und-deine-nerven-schont/</a><br />
<a href="https://creatronix.de/wie-baue-ich-eine-entwicklungsbegleitende-software-qualitatssicherung-auf/">https://creatronix.de/wie-baue-ich-eine-entwicklungsbegleitende-software-qualitatssicherung-auf/</a></p>
<p>The post <a href="https://creatronix.de/so-verwendest-du-google-test-in-deinem-cpp-projekt/">So verwendest du Google Test in deinem C++-Projekt</a> appeared first on <a href="https://creatronix.de">Creatronix</a>.</p>
]]></content:encoded>
					
		
		
			</item>
	</channel>
</rss>
