<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="de">
    <title>loki.dev</title>
    <subtitle>Der Blog von Torsten Zielke, Software-Architekt und Tech-Lead: praxisnahe Serien zu modernem Python-Tooling (uv, ruff, ty, prek), zu Linux und Rust sowie Gedanken zu KI-gestützter Softwareentwicklung.</subtitle>
    <link rel="self" type="application/atom+xml" href="https://loki.dev/atom.xml"/>
    <link rel="alternate" type="text/html" href="https://loki.dev"/>
    <generator uri="https://www.getzola.org/">Zola</generator>
    <updated>2026-08-08T13:59:23+00:00</updated>
    <id>https://loki.dev/atom.xml</id>
    <entry xml:lang="de">
        <title>Brooks&#39; Gesetz, jetzt mit Agenten</title>
        <published>2026-08-06T09:00:00+00:00</published>
        <updated>2026-08-08T13:59:23+00:00</updated>
        
        <author>
          <name>Unknown</name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://loki.dev/blog/brooks-law/"/>
        <id>https://loki.dev/blog/brooks-law/</id>
        
        <content type="html" xml:base="https://loki.dev/blog/brooks-law/">&lt;p&gt;Der Post wird jetzt wenig sinnstiftend, aber das Thema muss ich jetzt einfach
loswerden:
Folgendes Muster habe ich in mehreren Projekten gesehen, und es läuft immer gleich.&lt;/p&gt;
&lt;p&gt;Monate vergehen, ohne dass jemand einen verbindlichen Termin setzt. Dann steht
irgendwo ein Datum in einer Folie/Mail/Meeting, weil es nach außen zugesagt
wurde, und plötzlich muss ein halbfertiger Stand in sechs Wochen fertig sein.
In der Runde danach kommt aus dem C-Level der Vorschlag, den ich inzwischen
erwarte:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;... mehr Ressourcen.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Das Merkwürdige daran ist, dass der Vorschlag mit kürzerer Restlaufzeit sogar
noch sicherer kommt. Bei sechs Monaten diskutiert man Umfang, bei sechs Wochen
zwei zusätzliche Entwickler aus einem anderen Projekt. Fred Brooks hat 1975
aufgeschrieben, was dann passiert:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&quot;Adding manpower to a late software project makes it later.&quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Das ist Brooks&#39; Gesetz, aus &lt;em&gt;The Mythical Man-Month&lt;/em&gt;. Brooks hatte damals das
Material aus erster Hand: Er hat bei IBM die Entwicklung von OS/360 geleitet.
Mit tausenden Beteiligten wurde jeder Termin gerissen. Sein erkanntes Problem
ist die Einheit selbst. Kosten skalieren mit Personen mal Monaten, Fortschritt
tut das aber explizit nicht.&lt;/p&gt;
&lt;h2 id=&quot;warum-kurze-fristen-es-schlimmer-machen&quot;&gt;Warum kurze Fristen es schlimmer machen&lt;/h2&gt;
&lt;p&gt;Die Einarbeitung frisst genau das Fenster, das noch da ist. Wer neu dazukommt,
muss von den Leuten eingelernt werden, die bereits der Engpass sind. Bei sechs
Wochen Restlaufzeit ist das die ganze Restlaufzeit.&lt;/p&gt;
&lt;p&gt;Dazu kommt die Abstimmung. Bei n Personen gibt es &lt;code&gt;n(n-1)/2&lt;/code&gt; mögliche Kanäle,
vier Leute haben sechs, acht Leute haben achtundzwanzig. Jeder davon ist eine
Gelegenheit für zwei unterschiedliche Annahmen über dieselbe Schnittstelle, und
die fallen erst in der Integrationswoche auf.&lt;/p&gt;
&lt;p&gt;Teuer: Viel von der Arbeit ist sequenziell. Solange das Schema nicht migriert
ist, kann niemand den Endpoint darauf umbauen. Ein zweiter Entwickler wartet
dann genauso wie der erste, nur bezahlt.&lt;/p&gt;
&lt;h2 id=&quot;was-geholfen-hatte&quot;&gt;Was geholfen hätte&lt;/h2&gt;
&lt;p&gt;Brooks&#39; eigene Antwort ist unbequem und immer noch die einzige:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Umfang kürzen oder Termin verschieben.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;In den Projekten, die doch noch gut ausgegangen sind, stand am Anfang eine
ehrliche Liste, was zum Termin wirklich funktionieren muss. Der Rest kam ein
Release später, und niemand außerhalb des Teams hat es gemerkt.&lt;/p&gt;
&lt;p&gt;Und genau das wird immer als Option ausgeschlossen. Vielleicht sogar aus guten
Gründen (meistens etwas wie &quot;time2market beats quality&quot; oder ähnliches), aber
das ändert nichts daran, dass gewisse Dinge nicht mit der Realität vereinbar
sind.&lt;/p&gt;
&lt;h2 id=&quot;und-mit-agenten&quot;&gt;Und mit Agenten?&lt;/h2&gt;
&lt;p&gt;Seit ich mehrere Claude-Instanzen parallel laufen lasse, wird die Frage wieder
gestellt, diesmal ohne Personalkosten. Eine der drei Ursachen fällt tatsächlich
weg: Einarbeitung kostet fast nichts, der Kontext steht in &lt;code&gt;AGENTS.md&lt;/code&gt; (oder
&lt;code&gt;CLAUDE.md&lt;/code&gt; oder &lt;code&gt;GEMINI.md&lt;/code&gt;) und wird pro Agent wiederverwendet.
Die Sequenz bleibt, der Abhängigkeitsgraph interessiert sich nicht dafür, wer
davor sitzt. Und die Abstimmung kommt als Doppelarbeit zurück: zwei Agenten,
die denselben Helper refaktorieren, jeder für sich plausibel.&lt;/p&gt;
&lt;p&gt;Dazu ein Engpass, den Brooks nicht hatte. Ich bin der einzige, der Ergebnisse
annehmen darf. Drei Agenten parallel bringen mir etwas, bei zehn lese ich nur
noch Diffs und entscheide immer schlechter. Was mitskaliert, ist der
deterministische Teil des Reviews: Linter, Typprüfung, Tests, Pre-Commit-Hooks
urteilen bei zehn Agenten so schnell wie bei einem. Genau der Gedanke aus
&lt;a href=&quot;https://loki.dev/blog/modern-tooling-part-5-ai-harness/&quot;&gt;Teil 5 der
Tooling-Serie&lt;/a&gt; (ja, das
ist schamlose Eigenwerbung :D).&lt;/p&gt;
&lt;p&gt;Nach 51 Jahren ist damit eine der drei Ursachen weggefallen und eine vierte
dazugekommen. Für die Runde mit den sechs Wochen und dem Vorschlag von oben
rechnet sich das genauso wie 1975.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;(Der Großteil der Zahlen und Beispiele sind angepasste und basierend auf alten
Erfahrungen und habe nichts mit aktuellen Ereignissen zu tun)&lt;/em&gt;&lt;/p&gt;
</content>
        
    </entry>
    <entry xml:lang="de">
        <title>Moderne Werkzeuge Teil 5: Die Toolchain als Korsett für KI-Agenten</title>
        <published>2026-07-26T12:00:00+00:00</published>
        <updated>2026-07-26T12:00:00+00:00</updated>
        
        <author>
          <name>Unknown</name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://loki.dev/blog/modern-tooling-part-5-ai-harness/"/>
        <id>https://loki.dev/blog/modern-tooling-part-5-ai-harness/</id>
        
        <content type="html" xml:base="https://loki.dev/blog/modern-tooling-part-5-ai-harness/">&lt;p&gt;In &lt;a href=&quot;https://loki.dev/blog/modern-tooling-part-1-uv/&quot;&gt;Teil 1&lt;/a&gt; habe ich von zwei
Umbrüchen geschrieben: das Python-Ökosystem wird auf Rust-basierte Werkzeuge
umgestellt, und immer mehr Code entsteht durch KI. Die vier Teile davor haben
den ersten Umbruch abgearbeitet. In diesem Bonus-Teil kommt der zweite dazu,
denn beide passen besser zusammen, als es zunächst aussieht.&lt;/p&gt;
&lt;p&gt;Kurz gesagt: Dieselben &lt;code&gt;uv&lt;/code&gt;-, &lt;code&gt;ruff&lt;/code&gt;-, &lt;code&gt;ty&lt;/code&gt;- und &lt;code&gt;prek&lt;/code&gt;-Checks, die mir die
Arbeit erleichtern, sind auch das Korsett, das einen KI-Agenten produktiv und
vertrauenswürdig(er) macht.&lt;/p&gt;
&lt;h3 id=&quot;warum-ein-agent-leitplanken-braucht&quot;&gt;Warum ein Agent Leitplanken braucht&lt;/h3&gt;
&lt;p&gt;Ein KI-Agent schreibt in Sekunden plausibel aussehenden Code. Plausibel heißt
aber nicht korrekt: Er erfindet APIs, übersieht &lt;code&gt;None&lt;/code&gt;/&lt;code&gt;null&lt;/code&gt;-Fälle, ignoriert die
Architekturgrenzen des Projekts oder baut sich eben mal eine neue Abhängigkeit
ein. Wer das alles per Hand reviewen will, hat den Geschwindigkeitsgewinn sofort
wieder verloren.&lt;/p&gt;
&lt;p&gt;Statt dem Agenten mehr zu vertrauen, gebe ich ihm eine Umgebung, in der Fehler
sofort und deterministisch auffallen. Genau dafür habe ich die Toolchain ohnehin
schon aufgebaut (und auch vor KI schon so genutzt).&lt;/p&gt;
&lt;h3 id=&quot;dieselben-checks-jetzt-als-feedback-loop-fur-den-agenten&quot;&gt;Dieselben Checks, jetzt als Feedback-Loop für den Agenten&lt;/h3&gt;
&lt;p&gt;Für den Agenten brauche ich dafür kein neues Setup. Die Checks aus den Teilen 2
bis 4 sind schon eine maschinenlesbare, deterministische Wahrheit über den Code:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;uv run ruff check --fix .&lt;/code&gt; räumt auf und meldet, was es nicht selbst fixen kann&lt;/li&gt;
&lt;li&gt;&lt;code&gt;uv run ty check&lt;/code&gt; fängt Typ- und &lt;code&gt;None&lt;/code&gt;-Fehler ab, bevor sie Laufzeitfehler werden&lt;/li&gt;
&lt;li&gt;&lt;code&gt;uv run bandit&lt;/code&gt; und &lt;code&gt;import-linter&lt;/code&gt; bewachen Sicherheit und Architektur&lt;/li&gt;
&lt;li&gt;&lt;code&gt;uv run pytest&lt;/code&gt; sagt, ob der Code überhaupt noch tut, was er soll&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Der Agent bekommt diese Befehle an die Hand (bei mir in einer &lt;code&gt;AGENTS.md&lt;/code&gt; bzw.
&lt;code&gt;CLAUDE.md&lt;/code&gt;/&lt;code&gt;GEMINI.md&lt;/code&gt; mit Hinweis auf die &lt;code&gt;AGENTS.md&lt;/code&gt;) und arbeitet in genau der
Schleife, die ich als Mensch auch nutze: schreiben, prüfen, den Fehlern folgen,
nachbessern, wiederholen. Der Unterschied ist nur, dass er die Schleife viel
häufiger und schneller durchläuft.&lt;/p&gt;
&lt;h3 id=&quot;warum-geschwindigkeit-zur-grundvoraussetzung-wird&quot;&gt;Warum Geschwindigkeit zur Grundvoraussetzung wird&lt;/h3&gt;
&lt;p&gt;In den bisherigen Teilen war Geschwindigkeit &quot;nur&quot; Komfort. Beim Agenten wird
sie zur Grundvoraussetzung. Ein Check, der 30 Sekunden dauert, killt eine
Agenten-Schleife genauso wie meine eigene, nur dass der Agent vielleicht
zwanzigmal pro Minute prüfen will. Erst weil &lt;code&gt;ruff&lt;/code&gt; und &lt;code&gt;ty&lt;/code&gt; im
Millisekundenbereich antworten, kann der Agent eng iterieren, statt nach jedem
Schritt minutenlang zu warten oder, schlimmer, die Checks ganz zu überspringen.&lt;/p&gt;
&lt;h3 id=&quot;das-korsett-regeln-die-der-agent-nicht-umgehen-kann&quot;&gt;Das Korsett: Regeln, die der Agent nicht umgehen kann&lt;/h3&gt;
&lt;p&gt;Meine &lt;code&gt;select = [&quot;ALL&quot;]&lt;/code&gt;-Strategie aus &lt;a href=&quot;https://loki.dev/blog/modern-tooling-part-2-ruff/&quot;&gt;Teil 2&lt;/a&gt;,
die strikte Typdisziplin aus &lt;a href=&quot;https://loki.dev/blog/modern-tooling-part-3-ty/&quot;&gt;Teil 3&lt;/a&gt;
und die Architekturregeln aus &lt;a href=&quot;https://loki.dev/blog/modern-tooling-part-4-prek/&quot;&gt;Teil 4&lt;/a&gt;
sind für einen Agenten wertvoller als für mich: Sie schreiben die Form des
Projekts fest. Der Agent darf produktiv sein, aber nur innerhalb dieser Form.&lt;/p&gt;
&lt;p&gt;Und es ist nicht verhandelbar: Der Pre-Commit-Hook blockt schlechten Code,
bevor er in die Historie kommt, und die CI fängt den Rest (auch das
&lt;code&gt;--no-verify&lt;/code&gt; eines übermotivierten Agenten). Der Agent kann noch so überzeugt
argumentieren, der rote Check bleibt rot.&lt;/p&gt;
&lt;h3 id=&quot;fazit&quot;&gt;Fazit&lt;/h3&gt;
&lt;p&gt;Dass die beiden Umbrüche aus Teil 1 zeitgleich passieren, ist kein Zufall. Eine
schnelle, strikte, deterministische Toolchain ist die Voraussetzung dafür, dass
KI-gestützte Entwicklung nicht in einem Berg plausibler, aber kaputter Diffs
endet. Die Werkzeuge, die mir die Arbeit erleichtern, sind dieselben, die einen
Agenten in der Spur halten.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;&lt;strong&gt;Die Serie:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://loki.dev/blog/modern-tooling-part-1-uv/&quot;&gt;Teil 1: uv&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://loki.dev/blog/modern-tooling-part-2-ruff/&quot;&gt;Teil 2: ruff&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://loki.dev/blog/modern-tooling-part-3-ty/&quot;&gt;Teil 3: ty&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://loki.dev/blog/modern-tooling-part-4-prek/&quot;&gt;Teil 4: prek&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Teil 5: Korsett für KI-Agenten (Bonus, dieser Teil)&lt;/li&gt;
&lt;/ul&gt;
</content>
        
    </entry>
    <entry xml:lang="de">
        <title>Moderne Werkzeuge Teil 4: Hochperformante Git Hooks mit prek</title>
        <published>2026-07-20T21:00:00+00:00</published>
        <updated>2026-07-20T21:00:00+00:00</updated>
        
        <author>
          <name>Unknown</name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://loki.dev/blog/modern-tooling-part-4-prek/"/>
        <id>https://loki.dev/blog/modern-tooling-part-4-prek/</id>
        
        <content type="html" xml:base="https://loki.dev/blog/modern-tooling-part-4-prek/">&lt;p&gt;Im Laufe dieser Serie habe ich meinen Python-Stack mit &lt;code&gt;uv&lt;/code&gt; (&lt;a href=&quot;https://loki.dev/blog/modern-tooling-part-1-uv/&quot;&gt;Teil
1&lt;/a&gt;), &lt;code&gt;ruff&lt;/code&gt; (&lt;a href=&quot;https://loki.dev/blog/modern-tooling-part-2-ruff/&quot;&gt;Teil
2&lt;/a&gt;) und &lt;code&gt;ty&lt;/code&gt; (&lt;a href=&quot;https://loki.dev/blog/modern-tooling-part-3-ty/&quot;&gt;Teil
3&lt;/a&gt;) neu aufgebaut. In &lt;strong&gt;Teil 4&lt;/strong&gt;
bringe ich dann alles zusammen: Die Checks sollen automatisch laufen, bevor
Code überhaupt ins Repository gelangt. Kein Mensch hat Bock, ständig diesen
Stack an Tools von Hand auszuführen.&lt;/p&gt;
&lt;h3 id=&quot;git-hooks-als-quality-gate&quot;&gt;Git Hooks als Quality Gate&lt;/h3&gt;
&lt;p&gt;Jeder Check, der erst in der CI scheitert, ist eine Verzögerung und reißt mich
aus meinen Tasks raus: schreiben, committen, pushen, fünf Minuten warten, rote
Pipeline, fixen, &quot;Fix linting&quot;-Commit, nochmal pushen. Ein Pre-Commit-Hook
verschiebt genau dieselben Checks an den frühestmöglichen Punkt: Fehler werden
behoben, &lt;em&gt;bevor&lt;/em&gt; sie in der Historie landen, und die CI wird vom Fehlerfinder
zum reinen Sicherheitsnetz.&lt;/p&gt;
&lt;h3 id=&quot;der-engpass-beim-klassischen-pre-commit&quot;&gt;Der Engpass beim klassischen pre-commit&lt;/h3&gt;
&lt;p&gt;Das &lt;a rel=&quot;external&quot; href=&quot;https://pre-commit.com&quot;&gt;&lt;code&gt;pre-commit&lt;/code&gt;&lt;/a&gt;-Framework ist seit Jahren ein
Standard dafür. Auch Tools wie lefthook oder husky sind super. prek ist aber
flott und dank Kompatibilität zu pre-commit mein Tool der Wahl. Denn pre-commit
hat einige Nachteile: Es ist selbst ein Python-Paket, isoliert jeden Hook in
einer eigenen virtuellen Umgebung und führt Hooks sequenziell aus. Das erste
&lt;code&gt;git commit&lt;/code&gt; nach einem frischen Clone kann Minuten dauern, während Umgebungen
aufgebaut werden.&lt;/p&gt;
&lt;h3 id=&quot;prek&quot;&gt;prek&lt;/h3&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https://github.com/j178/prek&quot;&gt;&lt;code&gt;prek&lt;/code&gt;&lt;/a&gt; ist eine Reimplementierung des
pre-commit-Frameworks in Rust, und der entscheidende Punkt ist: &lt;strong&gt;Es liest
dieselbe &lt;code&gt;.pre-commit-config.yaml&lt;/code&gt;&lt;/strong&gt;. Aber mit smarterem Caching und ohne den
Virtualenv-Overhead:&lt;/p&gt;
&lt;pre class=&quot;giallo z-code&quot; &gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4&quot;&gt;# Installation (ein statisches Binary, kein Python nötig)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-5&quot;&gt;uv&lt;/span&gt;&lt;span class=&quot;z-11&quot;&gt; tool install prek&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4&quot;&gt;# alternativ mit brew/pacman/apt/etc.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4&quot;&gt;# Git-Hook im Repository aktivieren (einmalig pro Clone)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-5&quot;&gt;prek&lt;/span&gt;&lt;span class=&quot;z-11&quot;&gt; install&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4&quot;&gt;# Hooks manuell ausführen&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-5&quot;&gt;prek&lt;/span&gt;&lt;span class=&quot;z-11&quot;&gt; run&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;                # gegen gestagte Dateien&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-5&quot;&gt;prek&lt;/span&gt;&lt;span class=&quot;z-11&quot;&gt; run&lt;/span&gt;&lt;span class=&quot;z-6&quot;&gt; --all-files&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;    # gegen die gesamte Codebasis&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-5&quot;&gt;prek&lt;/span&gt;&lt;span class=&quot;z-11&quot;&gt; run gitleaks&lt;/span&gt;&lt;span class=&quot;z-6&quot;&gt; --all-files&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;  # einen einzelnen Hook gezielt&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&quot;mein-hook-setup-im-detail&quot;&gt;Mein Hook-Setup im Detail&lt;/h3&gt;
&lt;p&gt;Die &lt;code&gt;.pre-commit-config.yaml&lt;/code&gt; meines Templates verkettet alles, was ich in
den letzten drei Teilen aufgebaut habe. Interessant ist dabei die
Unterscheidung zwischen zwei Hook-Arten:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Remote-Hooks&lt;/strong&gt; kommen aus fremden Repositories: die Basics wie
YAML/TOML-Validierung, Trailing Whitespace und der Secret-Scanner
&lt;a rel=&quot;external&quot; href=&quot;https://github.com/gitleaks/gitleaks&quot;&gt;gitleaks&lt;/a&gt;:&lt;/p&gt;
&lt;pre class=&quot;giallo z-code&quot; &gt;&lt;code data-lang=&quot;yaml&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;-&lt;/span&gt;&lt;span class=&quot;z-13&quot;&gt; repo&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span class=&quot;z-11&quot;&gt; https://github.com/pre-commit/pre-commit-hooks&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-13&quot;&gt;  rev&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span class=&quot;z-11&quot;&gt; v6.0.0&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-13&quot;&gt;  hooks&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  -&lt;/span&gt;&lt;span class=&quot;z-13&quot;&gt; id&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span class=&quot;z-11&quot;&gt; check-yaml&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  -&lt;/span&gt;&lt;span class=&quot;z-13&quot;&gt; id&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span class=&quot;z-11&quot;&gt; check-toml&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  -&lt;/span&gt;&lt;span class=&quot;z-13&quot;&gt; id&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span class=&quot;z-11&quot;&gt; check-merge-conflict&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  -&lt;/span&gt;&lt;span class=&quot;z-13&quot;&gt; id&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span class=&quot;z-11&quot;&gt; trailing-whitespace&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  -&lt;/span&gt;&lt;span class=&quot;z-13&quot;&gt; id&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span class=&quot;z-11&quot;&gt; end-of-file-fixer&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;-&lt;/span&gt;&lt;span class=&quot;z-13&quot;&gt; repo&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span class=&quot;z-11&quot;&gt; https://github.com/gitleaks/gitleaks&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-13&quot;&gt;  rev&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span class=&quot;z-11&quot;&gt; v8.30.1&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-13&quot;&gt;  hooks&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  -&lt;/span&gt;&lt;span class=&quot;z-13&quot;&gt; id&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span class=&quot;z-11&quot;&gt; gitleaks&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Lokale Hooks&lt;/strong&gt; dagegen laufen über &lt;code&gt;uv run&lt;/code&gt; in meiner Projektumgebung:
damit prüfen ruff, ty und bandit exakt in den Versionen, die in der
&lt;code&gt;uv.lock&lt;/code&gt; gepinnt sind, statt in irgendeiner separaten Hook-Umgebung:&lt;/p&gt;
&lt;pre class=&quot;giallo z-code&quot; &gt;&lt;code data-lang=&quot;yaml&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;-&lt;/span&gt;&lt;span class=&quot;z-13&quot;&gt; repo&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span class=&quot;z-11&quot;&gt; local&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-13&quot;&gt;  hooks&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  -&lt;/span&gt;&lt;span class=&quot;z-13&quot;&gt; id&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span class=&quot;z-11&quot;&gt; ruff-format&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-13&quot;&gt;    name&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span class=&quot;z-11&quot;&gt; ruff format&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-13&quot;&gt;    entry&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span class=&quot;z-11&quot;&gt; uv run ruff format&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-13&quot;&gt;    language&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span class=&quot;z-11&quot;&gt; system&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-13&quot;&gt;    types&lt;/span&gt;&lt;span&gt;: [&lt;/span&gt;&lt;span class=&quot;z-11&quot;&gt;python&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  -&lt;/span&gt;&lt;span class=&quot;z-13&quot;&gt; id&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span class=&quot;z-11&quot;&gt; ruff-check&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-13&quot;&gt;    name&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span class=&quot;z-11&quot;&gt; ruff check&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-13&quot;&gt;    entry&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span class=&quot;z-11&quot;&gt; uv run ruff check --fix&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-13&quot;&gt;    language&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span class=&quot;z-11&quot;&gt; system&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-13&quot;&gt;    types&lt;/span&gt;&lt;span&gt;: [&lt;/span&gt;&lt;span class=&quot;z-11&quot;&gt;python&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  -&lt;/span&gt;&lt;span class=&quot;z-13&quot;&gt; id&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span class=&quot;z-11&quot;&gt; ty&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-13&quot;&gt;    name&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span class=&quot;z-11&quot;&gt; ty-check&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-13&quot;&gt;    entry&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span class=&quot;z-11&quot;&gt; uv run ty check --verbose --output-format=full .&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-13&quot;&gt;    language&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span class=&quot;z-11&quot;&gt; system&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-13&quot;&gt;    types&lt;/span&gt;&lt;span&gt;: [&lt;/span&gt;&lt;span class=&quot;z-11&quot;&gt;python&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-13&quot;&gt;    pass_filenames&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span class=&quot;z-6&quot;&gt; false&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  -&lt;/span&gt;&lt;span class=&quot;z-13&quot;&gt; id&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span class=&quot;z-11&quot;&gt; bandit&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-13&quot;&gt;    name&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span class=&quot;z-11&quot;&gt; bandit&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-13&quot;&gt;    entry&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span class=&quot;z-11&quot;&gt; uv run bandit -r src/app -c pyproject.toml&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-13&quot;&gt;    language&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span class=&quot;z-11&quot;&gt; system&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-13&quot;&gt;    types&lt;/span&gt;&lt;span&gt;: [&lt;/span&gt;&lt;span class=&quot;z-11&quot;&gt;python&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-13&quot;&gt;    pass_filenames&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span class=&quot;z-6&quot;&gt; false&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Dazu kommt noch &lt;a rel=&quot;external&quot; href=&quot;https://import-linter.readthedocs.io&quot;&gt;import-linter&lt;/a&gt;, der
meine Architektur bewacht: Er schlägt fehl, sobald jemand aus der
Domain-Schicht heraus Infrastruktur importiert: Architekturregeln als
ausführbarer Check statt als Wiki-Seite.&lt;/p&gt;
&lt;h3 id=&quot;ein-commit-das-ganze-quality-gate&quot;&gt;Ein Commit, das ganze Quality Gate&lt;/h3&gt;
&lt;p&gt;Bei jedem &lt;code&gt;git commit&lt;/code&gt; läuft damit automatisch: Formatting, Linting mit
Auto-Fix, Type Checking, Security-Scan (bandit), Secret-Scan (gitleaks) und
die Architekturprüfung. Dank prek weitgehend parallel und in wenigen
Sekunden, schnell genug, dass niemand in Versuchung kommt, es abzuschalten.&lt;/p&gt;
&lt;p&gt;Für den Notfall gibt es trotzdem die Hintertür:&lt;/p&gt;
&lt;pre class=&quot;giallo z-code&quot; &gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-5&quot;&gt;git&lt;/span&gt;&lt;span class=&quot;z-11&quot;&gt; commit&lt;/span&gt;&lt;span class=&quot;z-6&quot;&gt; --no-verify&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Aber genau dafür (naja, oder für die Fälle, wo jemand &lt;code&gt;prek install&lt;/code&gt; vergessen
hat) ist die CI das Sicherheitsnetz: Dort laufen dieselben Checks
(&lt;code&gt;ruff format --check&lt;/code&gt;, &lt;code&gt;ruff check&lt;/code&gt;, &lt;code&gt;ty check&lt;/code&gt;, &lt;code&gt;bandit&lt;/code&gt;, &lt;code&gt;lint-imports&lt;/code&gt;,
&lt;code&gt;pytest&lt;/code&gt;) noch einmal, nur eben als letzte Instanz, nicht als erste.&lt;/p&gt;
&lt;h3 id=&quot;fazit-der-serie&quot;&gt;Fazit der Serie&lt;/h3&gt;
&lt;p&gt;Diese vier Tools &lt;code&gt;uv&lt;/code&gt;, &lt;code&gt;ruff&lt;/code&gt;, &lt;code&gt;ty&lt;/code&gt; und &lt;code&gt;prek&lt;/code&gt; ersetzen jeweils einen
langsamen, fragmentierten Teil der Python-Toolchain durch ein schnelles
Rust-Binary, und bleiben dabei kompatibel zu dem, was sie ablösen
(&lt;code&gt;pyproject.toml&lt;/code&gt;, Black-Formatierung, &lt;code&gt;.pre-commit-config.yaml&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;Und wenn ich ehrlich bin, sind sie meist die ersten Tools ihrer Art, denn eine
Codebasis, die hier schon perfekt war, habe ich bisher nicht gesehen.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;&lt;strong&gt;Die Serie:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://loki.dev/blog/modern-tooling-part-1-uv/&quot;&gt;Teil 1: uv&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://loki.dev/blog/modern-tooling-part-2-ruff/&quot;&gt;Teil 2: ruff&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://loki.dev/blog/modern-tooling-part-3-ty/&quot;&gt;Teil 3: ty&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Teil 4: prek (dieser Teil)&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://loki.dev/blog/modern-tooling-part-5-ai-harness/&quot;&gt;Teil 5: Korsett für KI-Agenten (Bonus)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content>
        
    </entry>
    <entry xml:lang="de">
        <title>Moderne Werkzeuge Teil 3: Die Zukunft des Type Checkings mit ty</title>
        <published>2026-07-20T07:32:00+00:00</published>
        <updated>2026-07-20T07:32:00+00:00</updated>
        
        <author>
          <name>Unknown</name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://loki.dev/blog/modern-tooling-part-3-ty/"/>
        <id>https://loki.dev/blog/modern-tooling-part-3-ty/</id>
        
        <content type="html" xml:base="https://loki.dev/blog/modern-tooling-part-3-ty/">&lt;p&gt;Bisher habe ich in dieser Serie das Paketmanagement mit &lt;code&gt;uv&lt;/code&gt;
(&lt;a href=&quot;https://loki.dev/blog/modern-tooling-part-1-uv/&quot;&gt;Teil 1&lt;/a&gt;) und das Linting mit
&lt;code&gt;ruff&lt;/code&gt; (&lt;a href=&quot;https://loki.dev/blog/modern-tooling-part-2-ruff/&quot;&gt;Teil 2&lt;/a&gt;) optimiert.
In &lt;strong&gt;Teil 3&lt;/strong&gt; kommt der anspruchsvollste Teil der statischen Analyse: das
Type Checking.&lt;/p&gt;
&lt;h3 id=&quot;warum-type-checking&quot;&gt;Warum Type Checking?&lt;/h3&gt;
&lt;p&gt;So richtig in Kontakt mit Typechecking bin ich damals über phpstan/psalm in PHP
gekommen. Das fühlte sich an, als käme die Validierung, die ich aus anderen
Sprachen kannte, endlich auch in den Scriptsprachen an. JA: Es ist etwas mehr
Arbeit, und natürlich sind Arrays (PHP) bzw. Listen nicht mehr ganz so flexibel.
Einfach alles reinschmeißen ist aber ohnehin keine gute Idee. Für mich haben
Typechecker also sinnvolles Verhalten erzwungen, statt zufällig Regeln
anzuwenden, die mir das Leben schwer machen.&lt;/p&gt;
&lt;p&gt;Der Hauptgrund für mich ist aber, dass Typechecker in Scriptsprachen einfach
komplette Kategorien an Fehlern auslöschen. In Kombination mit Lintern sollte
man in Python extrem viel weniger (oder sogar gar keine) &lt;code&gt;None&lt;/code&gt;-Fehler mehr
haben. Bei konsequentem Einsatz umgeht man viele Probleme, z. B. nicht
abgefangene logische Branches in &lt;code&gt;match&lt;/code&gt;/&lt;code&gt;if-elif-else&lt;/code&gt;-Kaskaden.&lt;/p&gt;
&lt;p&gt;Und schließlich: Code Completion. Mit KI ist das natürlich weniger wichtig, aber
jede moderne IDE kann diese Typehints auswerten und damit sinnvolle Vorschläge
machen. Der Erfolg von FastAPI/pydantic beruht zum Teil genau darauf, dass die
DX (Developer Experience) so gut ist!&lt;/p&gt;
&lt;p&gt;In meinem Template mit hexagonaler Architektur sind Typen sogar tragend: die
Ports sind als abstrakte, vollständig typisierte Interfaces definiert:&lt;/p&gt;
&lt;pre class=&quot;giallo z-code&quot; &gt;&lt;code data-lang=&quot;python&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-1 z-i&quot;&gt;class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span class=&quot;z-5 z-u&quot;&gt;ItemRepository&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span class=&quot;z-6&quot;&gt;ABC&lt;/span&gt;&lt;span&gt;):&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-5&quot;&gt;    @abstractmethod&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-1 z-i&quot;&gt;    async def&lt;/span&gt;&lt;span class=&quot;z-5&quot;&gt; save&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span class=&quot;z-14 z-i&quot;&gt;self&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span class=&quot;z-14 z-i&quot;&gt; item&lt;/span&gt;&lt;span&gt;: Item) -&amp;gt; Item:&lt;/span&gt;&lt;span class=&quot;z-6&quot;&gt; ...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-5&quot;&gt;    @abstractmethod&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-1 z-i&quot;&gt;    async def&lt;/span&gt;&lt;span class=&quot;z-5&quot;&gt; by_id&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span class=&quot;z-14 z-i&quot;&gt;self&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span class=&quot;z-14 z-i&quot;&gt; item_id&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span class=&quot;z-6&quot;&gt; UUID&lt;/span&gt;&lt;span&gt;) -&amp;gt; Item&lt;/span&gt;&lt;span class=&quot;z-13&quot;&gt; |&lt;/span&gt;&lt;span class=&quot;z-6&quot;&gt; None&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span class=&quot;z-6&quot;&gt; ...&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ein Type Checker stellt sicher, dass jeder Adapter diesen Vertrag exakt
erfüllt, und dass niemand vergisst, den &lt;code&gt;None&lt;/code&gt;-Fall von &lt;code&gt;by_id&lt;/code&gt; zu behandeln.&lt;/p&gt;
&lt;p&gt;Das Problem war nie der Nutzen, sondern der Preis: &lt;code&gt;mypy&lt;/code&gt; wird auf wachsenden
Codebasen spürbar langsam. Und ein Check, der 30 Sekunden dauert, wird lokal
nicht mehr ausgeführt, er wandert in die CI, und Typfehler fallen erst nach
dem Push auf. Genau das Feedback-Loop-Problem, das &lt;code&gt;ruff&lt;/code&gt; fürs Linting gelöst
hat, existierte beim Type Checking weiter.&lt;/p&gt;
&lt;h3 id=&quot;ty&quot;&gt;ty&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;ty&lt;/code&gt; ist Astrals Antwort darauf: ein in Rust geschriebener, auf massive
Parallelität ausgelegter Type Checker, der sich direkt in die
&lt;code&gt;uv&lt;/code&gt;/&lt;code&gt;ruff&lt;/code&gt;-Toolchain einfügt. Der Check läuft so schnell, dass er problemlos
in jeden Pre-Commit-Hook passt:&lt;/p&gt;
&lt;pre class=&quot;giallo z-code&quot; &gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-5&quot;&gt;uv&lt;/span&gt;&lt;span class=&quot;z-11&quot;&gt; run ty check&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ein typischer Fund sieht dann so aus: hier vergisst jemand, dass
&lt;code&gt;by_id&lt;/code&gt; auch &lt;code&gt;None&lt;/code&gt; liefern kann:&lt;/p&gt;
&lt;pre class=&quot;giallo z-code&quot; &gt;&lt;code data-lang=&quot;python&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;item&lt;/span&gt;&lt;span class=&quot;z-13&quot;&gt; = await&lt;/span&gt;&lt;span&gt; repository.by_id(item_id)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-1&quot;&gt;print&lt;/span&gt;&lt;span&gt;(item.name)&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;  # error: `name` on `Item | None`, `None` hat kein `name`&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Zur Laufzeit wäre das ein &lt;code&gt;AttributeError&lt;/code&gt; in Produktion. Mit &lt;code&gt;ty&lt;/code&gt; ist es eine
rote Zeile, bevor der Commit überhaupt entsteht, je nach CI-Tooling sogar ein
verhinderter Commit oder Merge.&lt;/p&gt;
&lt;h3 id=&quot;meine-typdisziplin&quot;&gt;Meine Typdisziplin&lt;/h3&gt;
&lt;p&gt;Wichtig zur Einordnung: Dass jede Funktion, Variable und Klasse vollständig
typisiert sein muss und &lt;code&gt;Any&lt;/code&gt; die absolute Ausnahme bleibt, ist keine
Eigenschaft von &lt;code&gt;ty&lt;/code&gt;, sondern eine &lt;strong&gt;Projektregel&lt;/strong&gt; meines Templates. &lt;code&gt;ty&lt;/code&gt; ist
das Werkzeug, das sie durchsetzbar macht, gemeinsam mit den &lt;code&gt;ANN&lt;/code&gt;-Regeln von
&lt;code&gt;ruff&lt;/code&gt;, &lt;a rel=&quot;external&quot; href=&quot;https://docs.astral.sh/ruff/rules/#flake8-annotations-ann&quot;&gt;die fehlende Annotationen anmahnen&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Im Pre-Commit-Hook läuft der Check über die gesamte Codebasis:&lt;/p&gt;
&lt;pre class=&quot;giallo z-code&quot; &gt;&lt;code data-lang=&quot;yaml&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;-&lt;/span&gt;&lt;span class=&quot;z-13&quot;&gt; id&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span class=&quot;z-11&quot;&gt; ty&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-13&quot;&gt;  name&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span class=&quot;z-11&quot;&gt; ty-check&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-13&quot;&gt;  entry&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span class=&quot;z-11&quot;&gt; uv run ty check --verbose --output-format=full .&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-13&quot;&gt;  language&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span class=&quot;z-11&quot;&gt; system&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-13&quot;&gt;  types&lt;/span&gt;&lt;span&gt;: [&lt;/span&gt;&lt;span class=&quot;z-11&quot;&gt;python&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-13&quot;&gt;  pass_filenames&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span class=&quot;z-6&quot;&gt; false&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;pass_filenames: false&lt;/code&gt; ist hier bewusst gesetzt: Type Checking ergibt nur
über das ganze Projekt Sinn, denn eine Änderung in Datei A kann einen
Typfehler in Datei B auslösen. Dank der Geschwindigkeit von &lt;code&gt;ty&lt;/code&gt; kann ich mir
den Voll-Check bei jedem Commit leisten. Nice :).&lt;/p&gt;
&lt;h3 id=&quot;im-editor&quot;&gt;Im Editor&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;ty&lt;/code&gt; ist nicht nur ein CLI-Tool, sondern bringt einen eigenen Language Server
mit: Autocompletion, Hover, Go-to-Definition und Auto-Imports, alles in
&lt;code&gt;ty&lt;/code&gt;-Geschwindigkeit.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;VS Code:&lt;/strong&gt; Es gibt eine offizielle
&lt;a rel=&quot;external&quot; href=&quot;https://docs.astral.sh/ty/editors/&quot;&gt;ty-Extension&lt;/a&gt; von Astral. Sie schaltet den
Language Server der Python-Extension automatisch ab (setzt
&lt;code&gt;python.languageServer&lt;/code&gt; auf &lt;code&gt;None&lt;/code&gt;), damit nicht zwei Language Server
gleichzeitig laufen.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;PyCharm:&lt;/strong&gt; Ab Version 2025.3 ist &lt;code&gt;ty&lt;/code&gt; direkt eingebaut. Unter
&lt;em&gt;Settings → Python | Tools → ty&lt;/em&gt; die Checkbox &lt;em&gt;Enable&lt;/em&gt; setzen. PyCharm findet die
im Interpreter installierte &lt;code&gt;ty&lt;/code&gt;-Version dann selbst.&lt;/p&gt;
&lt;p&gt;Darunter steckt immer derselbe Language Server, den man auch manuell starten
kann (&lt;code&gt;uv run ty server&lt;/code&gt;), z. B. für Neovim.&lt;/p&gt;
&lt;h3 id=&quot;einschrankungen&quot;&gt;Einschränkungen&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;ty&lt;/code&gt; ist jung. Mein Template pinnt es auf &lt;code&gt;ty==0.*&lt;/code&gt;: die Null vor dem Punkt ist
im Open-Source-Umfeld nicht immer aussagekräftig. Hier stimmt sie aber durchaus,
&lt;code&gt;ty&lt;/code&gt; ist bis Version &lt;code&gt;0.0.61&lt;/code&gt; offiziell noch eine Beta. Meiner Meinung nach
(sehr subjektiv!) ist es aber bereits production ready, der Speed-Vorteil ist es
mir einfach wert.&lt;/p&gt;
&lt;p&gt;In meinen Architecture Decision Records markiere ich die Einführung trotzdem als
&lt;em&gt;Experimental&lt;/em&gt;: Ich evaluiere &lt;code&gt;ty&lt;/code&gt; als primären Type Checker und beobachte, wie
schnell es bei komplexeren Typing-PEPs (Generics, &lt;code&gt;ParamSpec&lt;/code&gt;, Overloads) zu
&lt;code&gt;mypy&lt;/code&gt;/&lt;code&gt;pyright&lt;/code&gt; aufschließt.&lt;/p&gt;
&lt;p&gt;Für ein Template und Greenfield-Projekte ist das Risiko überschaubar, die
Typkonstrukte sind dort selten exotisch. Wer eine große Bestands-Codebasis mit
ausgefeilten &lt;code&gt;mypy&lt;/code&gt;-Plugin-Setups hat, sollte den Wechsel dagegen bewusst
abwägen: &lt;code&gt;mypy&lt;/code&gt; ist bei der Regelabdeckung aktuell noch ausgefeilter.&lt;/p&gt;
&lt;h3 id=&quot;fazit&quot;&gt;Fazit&lt;/h3&gt;
&lt;p&gt;Der eigentliche Gewinn von &lt;code&gt;ty&lt;/code&gt; ist nicht die bessere Analyse, sondern ein Type
Checker, der flott läuft: lokal, bei jedem Commit, ohne dass man warten muss.
Typdisziplin hält man nur durch, wenn das Feedback schnell kommt, und genau das
liefert &lt;code&gt;ty&lt;/code&gt;.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;&lt;strong&gt;Die Serie:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://loki.dev/blog/modern-tooling-part-1-uv/&quot;&gt;Teil 1: uv&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://loki.dev/blog/modern-tooling-part-2-ruff/&quot;&gt;Teil 2: ruff&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Teil 3: ty (dieser Teil)&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://loki.dev/blog/modern-tooling-part-4-prek/&quot;&gt;Teil 4: prek&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://loki.dev/blog/modern-tooling-part-5-ai-harness/&quot;&gt;Teil 5: Korsett für KI-Agenten (Bonus)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content>
        
    </entry>
    <entry xml:lang="de">
        <title>Moderne Werkzeuge Teil 2: Einheitliches Linting und Formatting mit ruff</title>
        <published>2026-07-16T17:14:00+00:00</published>
        <updated>2026-07-16T17:14:00+00:00</updated>
        
        <author>
          <name>Unknown</name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://loki.dev/blog/modern-tooling-part-2-ruff/"/>
        <id>https://loki.dev/blog/modern-tooling-part-2-ruff/</id>
        
        <content type="html" xml:base="https://loki.dev/blog/modern-tooling-part-2-ruff/">&lt;p&gt;Nachdem ich in &lt;a href=&quot;https://loki.dev/blog/modern-tooling-part-1-uv/&quot;&gt;Teil 1&lt;/a&gt; das
Paketmanagement mit &lt;code&gt;uv&lt;/code&gt; aufgeräumt habe, will ich mich heute in Teil 2 um
&lt;strong&gt;ruff&lt;/strong&gt; kümmern.&lt;/p&gt;
&lt;h3 id=&quot;toolchain-suppe&quot;&gt;Toolchain-Suppe&lt;/h3&gt;
&lt;p&gt;&quot;Code-Qualität&quot; hieß lange, vier bis fünf Tools hintereinander zu
schalten:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Black für das Formatting&lt;/li&gt;
&lt;li&gt;Isort für die Import-Sortierung&lt;/li&gt;
&lt;li&gt;Flake8 (oder pylint) für das Linting (plus ein Dutzend Plugins)&lt;/li&gt;
&lt;li&gt;pyupgrade für moderne Syntax&lt;/li&gt;
&lt;li&gt;und Bandit für Sicherheitschecks&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Auch hier galt: Jedes Tool hat seine eigene Konfigurationsdatei, eine eigene
Version und eigene Konflikte mit: Black und Isort mussten z. B. explizit
aufeinander abgestimmt werden, damit sie sich nicht gegenseitig die Imports
umformatierten.&lt;/p&gt;
&lt;p&gt;Und es war langsam. Auf mittleren Codebasen liefen diese Checks sekunden- bzw.
teilweise sogar minutenlang. Imho zu langsam, um sie bei jedem Speichern oder
Commit auszuführen. Ich hatte sie trotzdem immer aktiv, war dann meist schon
beim nächsten Problem und wurde prompt wieder rausgeworfen, weil es irgendein
Trivialproblem gab. Also mehr als einmal deaktiviert und am Ende vergessen, sie
nochmal auszuführen, nur um dann von einer roten CI-Pipeline genervt zu werden.&lt;/p&gt;
&lt;h3 id=&quot;ruff&quot;&gt;ruff!&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;ruff&lt;/code&gt; ist Linter &lt;strong&gt;und&lt;/strong&gt; Formatter in einem Rust-Binary. Es implementiert die
Regeln von Flake8 (inklusive der populären Plugins), Isort, pyupgrade und
großen Teilen von Bandit, und formatiert Black-kompatibel (mit minimalen
Abweichungen). Die Laufzeit liegt selbst auf großen Codebasen im
Millisekundenbereich, was einen entscheidenden Unterschied macht:&lt;/p&gt;
&lt;p&gt;Checks, die instant sind, laufen bei jedem Speichern im Editor, bei jedem
Commit und in der CI. Checks, die Sekunden dauern, laufen irgendwann nur noch
in der CI. Zusammen mit &lt;code&gt;--fix&lt;/code&gt; einfach geniales Tooling, das auch unabhängig
von der IDE funktioniert &amp;lt;3.&lt;/p&gt;
&lt;p&gt;Die beiden Befehle, die du brauchst:&lt;/p&gt;
&lt;pre class=&quot;giallo z-code&quot; &gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4&quot;&gt;# Linting-Fehler finden und automatisch beheben, wo möglich&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-5&quot;&gt;uv&lt;/span&gt;&lt;span class=&quot;z-11&quot;&gt; run ruff check&lt;/span&gt;&lt;span class=&quot;z-6&quot;&gt; --fix&lt;/span&gt;&lt;span class=&quot;z-11&quot;&gt; .&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4&quot;&gt;# Die Codebasis formatieren&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-5&quot;&gt;uv&lt;/span&gt;&lt;span class=&quot;z-11&quot;&gt; run ruff format .&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In der CI drehe ich den Spieß um, dort wird nichts repariert, sondern nur
geprüft:&lt;/p&gt;
&lt;pre class=&quot;giallo z-code&quot; &gt;&lt;code data-lang=&quot;yaml&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;-&lt;/span&gt;&lt;span class=&quot;z-13&quot;&gt; name&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span class=&quot;z-11&quot;&gt; Formatting Check (Ruff)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-13&quot;&gt;  run&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span class=&quot;z-11&quot;&gt; uv run ruff format --check .&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;-&lt;/span&gt;&lt;span class=&quot;z-13&quot;&gt; name&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span class=&quot;z-11&quot;&gt; Linting Check (Ruff)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-13&quot;&gt;  run&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span class=&quot;z-11&quot;&gt; uv run ruff check .&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&quot;meine-konfiguration-opt-out-statt-opt-in&quot;&gt;Meine Konfiguration: Opt-out statt Opt-in&lt;/h3&gt;
&lt;p&gt;Die meisten Projekte aktivieren eine Handvoll Regelgruppen und ergänzen nach
und nach. Mein Template geht den umgekehrten Weg: &lt;strong&gt;alle&lt;/strong&gt; Regeln an, und
jede Ausnahme muss begründet werden. Das zwingt dazu, sich bei jedem &lt;code&gt;ignore&lt;/code&gt;
bewusst zu entscheiden, und dokumentiert die Entscheidung gleich mit:&lt;/p&gt;
&lt;pre class=&quot;giallo z-code&quot; &gt;&lt;code data-lang=&quot;toml&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4&quot;&gt;# Teil der pyproject.toml&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;[tool.ruff.lint]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4&quot;&gt;# Ich will strikt sein und nur bewusst abwählen!&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;select = [&lt;/span&gt;&lt;span class=&quot;z-11&quot;&gt; &amp;quot;ALL&amp;quot;&lt;/span&gt;&lt;span&gt; ]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;fixable = [&lt;/span&gt;&lt;span class=&quot;z-11&quot;&gt; &amp;quot;ALL&amp;quot;&lt;/span&gt;&lt;span&gt; ]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;ignore = [&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4&quot;&gt;  # strict&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-11&quot;&gt;  &amp;quot;D102&amp;quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;    # würde Docstrings für jede öffentliche Methode erzwingen&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-11&quot;&gt;  &amp;quot;D203&amp;quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;    # inkompatibel zu D211 (kein Leerzeile vor Klassen-Docstring)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-11&quot;&gt;  &amp;quot;EM101&amp;quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;   # würde erzwingen, Exception-Texte erst in Variablen zu speichern&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4&quot;&gt;  # inkompatibel mit &amp;quot;ruff format&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-11&quot;&gt;  &amp;quot;COM812&amp;quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4&quot;&gt;  # temporär - als Beispiel&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-11&quot;&gt;  &amp;quot;TC003&amp;quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;   # würde TYPE_CHECKING-Blöcke erzwingen.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Der Kommentar hinter jeder Regel ist kein Nice-to-have, sondern der Kern der
Strategie: In einem Jahr weiß niemand mehr, warum &lt;code&gt;COM812&lt;/code&gt; aus ist, außer es
steht daneben.&lt;/p&gt;
&lt;h3 id=&quot;per-file-ignores-tests-sind-anders&quot;&gt;Per-File-Ignores: Tests sind anders&lt;/h3&gt;
&lt;p&gt;Regeln, die im Produktivcode sinnvoll sind, sind in Tests oft Lärm. &lt;code&gt;assert&lt;/code&gt;
ist in Tests der Normalfall (im Produktivcode verbietet ihn die
Bandit-Regel &lt;code&gt;S101&lt;/code&gt;), und &quot;Magic Numbers&quot; in einem Testfall sind schlicht die
Testdaten:&lt;/p&gt;
&lt;pre class=&quot;giallo z-code&quot; &gt;&lt;code data-lang=&quot;toml&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4&quot;&gt;# Teil der pyproject.toml&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;[tool.ruff.lint.per-file-ignores]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;&amp;quot;tests/*&amp;quot; = [&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-11&quot;&gt;    &amp;quot;ANN201&amp;quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;   # Rückgabe-Annotationen für Testfunktionen: unnötig&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-11&quot;&gt;    &amp;quot;PLR2004&amp;quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;  # Magic Values in Vergleichen: das SIND die Testdaten&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-11&quot;&gt;    &amp;quot;S101&amp;quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;     # assert ist in pytest essentiell&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&quot;import-sortierung-inklusive&quot;&gt;Import-Sortierung inklusive&lt;/h3&gt;
&lt;p&gt;Isort ist als Regelgruppe &lt;code&gt;I&lt;/code&gt; gleich mit an Bord. Damit die Sortierung auch in
Grenzfällen deterministisch bleibt, deklariere ich explizit, was third-party
und was mein eigener Code ist:&lt;/p&gt;
&lt;pre class=&quot;giallo z-code&quot; &gt;&lt;code data-lang=&quot;toml&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;[tool.ruff.lint.isort]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;known-third-party = [&lt;/span&gt;&lt;span class=&quot;z-11&quot;&gt;&amp;quot;fastapi&amp;quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span class=&quot;z-11&quot;&gt; &amp;quot;pydantic&amp;quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span class=&quot;z-11&quot;&gt; &amp;quot;starlette&amp;quot;&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;known-local-folder = [&lt;/span&gt;&lt;span class=&quot;z-11&quot;&gt;&amp;quot;app&amp;quot;&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Kurze Erinnerung: &lt;code&gt;isort&lt;/code&gt; sorgt dafür, dass die Importblöcke grob in 3
Kategorien unterteilt sind:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&quot;batteries included&quot; Imports, z. B.: &lt;code&gt;from math import sqrt&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;3rd party Imports: &lt;code&gt;from fastapi import FastAPI&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Projektimports: &lt;code&gt;from my_project.domain import ImportantClass&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Innerhalb der Blöcke dann alphabetisch.&lt;/p&gt;
&lt;h3 id=&quot;im-editor-feedback-beim-tippen&quot;&gt;Im Editor: Feedback beim Tippen&lt;/h3&gt;
&lt;p&gt;Der eigentliche Gewinn kommt, wenn &lt;code&gt;ruff&lt;/code&gt; schon beim Tippen läuft und nicht erst
beim Commit.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;VS Code:&lt;/strong&gt; Die offizielle
&lt;a rel=&quot;external&quot; href=&quot;https://marketplace.visualstudio.com/items?itemName=charliermarsh.ruff&quot;&gt;Ruff-Extension&lt;/a&gt;
(von charliermarsh/Astral) übernimmt Linting und Formatting. Wichtig ist mir,
dass sie die im Projekt gepinnte Version nutzt: &lt;code&gt;ruff.importStrategy&lt;/code&gt; steht per
Default auf &lt;code&gt;fromEnvironment&lt;/code&gt;, greift also auf das &lt;code&gt;ruff&lt;/code&gt; aus der &lt;code&gt;uv&lt;/code&gt;-Umgebung
zu statt auf die mitgelieferte Version.&lt;/p&gt;
&lt;pre class=&quot;giallo z-code&quot; &gt;&lt;code data-lang=&quot;json&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4&quot;&gt;// .vscode/settings.json&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-1 z-i&quot;&gt;  &amp;quot;[python]&amp;quot;&lt;/span&gt;&lt;span&gt;: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-1 z-i&quot;&gt;    &amp;quot;editor.defaultFormatter&amp;quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span class=&quot;z-10&quot;&gt; &amp;quot;charliermarsh.ruff&amp;quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-1 z-i&quot;&gt;    &amp;quot;editor.formatOnSave&amp;quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span class=&quot;z-6&quot;&gt; true&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-1 z-i&quot;&gt;    &amp;quot;editor.codeActionsOnSave&amp;quot;&lt;/span&gt;&lt;span&gt;: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-1 z-i&quot;&gt;      &amp;quot;source.fixAll.ruff&amp;quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span class=&quot;z-10&quot;&gt; &amp;quot;explicit&amp;quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-1 z-i&quot;&gt;      &amp;quot;source.organizeImports.ruff&amp;quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span class=&quot;z-10&quot;&gt; &amp;quot;explicit&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-1 z-i&quot;&gt;  &amp;quot;ruff.importStrategy&amp;quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span class=&quot;z-10&quot;&gt; &amp;quot;fromEnvironment&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Das &lt;code&gt;.ruff&lt;/code&gt;-Suffix an den Code-Actions sorgt dafür, dass wirklich &lt;code&gt;ruff&lt;/code&gt; fixt
und die Imports sortiert und nicht versehentlich ein anderer Formatter.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;PyCharm:&lt;/strong&gt; Ab Version 2025.3 ist die Ruff-Unterstützung eingebaut. Unter
&lt;em&gt;Settings → Tools → Ruff&lt;/em&gt; aktivieren, dann greifen &lt;em&gt;Reformat Code&lt;/em&gt; und &lt;em&gt;Optimize
Imports&lt;/em&gt; auf &lt;code&gt;ruff&lt;/code&gt; zu. Für ältere Versionen gibt es das
&lt;a rel=&quot;external&quot; href=&quot;https://plugins.jetbrains.com/plugin/20574-ruff&quot;&gt;Ruff-Plugin von koxudaxi&lt;/a&gt;. In
beiden Fällen lohnt es sich, auf das &lt;code&gt;ruff&lt;/code&gt; aus der Projektumgebung zu zeigen
statt auf eine global installierte Version.&lt;/p&gt;
&lt;h3 id=&quot;fazit&quot;&gt;Fazit&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;ruff&lt;/code&gt; ersetzt vier Konfigurationsdateien durch einen Block in der
&lt;code&gt;pyproject.toml&lt;/code&gt; und macht Linting so schnell, dass es keine Ausrede mehr
gibt, es nicht überall laufen zu lassen. Die Opt-out-Strategie mit
&lt;code&gt;select = [&quot;ALL&quot;]&lt;/code&gt; ist dabei bewusst radikal: Sie kostet beim Aufsetzen etwas
Zeit, liefert dafür aber ein Regelwerk, in dem jede Ausnahme ein
dokumentierter Entschluss ist statt ein historischer Zufall.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;&lt;strong&gt;Die Serie:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://loki.dev/blog/modern-tooling-part-1-uv/&quot;&gt;Teil 1: uv&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Teil 2: ruff (dieser Teil)&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://loki.dev/blog/modern-tooling-part-3-ty/&quot;&gt;Teil 3: ty&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://loki.dev/blog/modern-tooling-part-4-prek/&quot;&gt;Teil 4: prek&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://loki.dev/blog/modern-tooling-part-5-ai-harness/&quot;&gt;Teil 5: Korsett für KI-Agenten (Bonus)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content>
        
    </entry>
    <entry xml:lang="de">
        <title>Moderne Werkzeuge Teil 1: Blitzschnelles Package Management mit uv</title>
        <published>2026-07-13T19:50:00+00:00</published>
        <updated>2026-07-16T15:15:29+00:00</updated>
        
        <author>
          <name>Unknown</name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://loki.dev/blog/modern-tooling-part-1-uv/"/>
        <id>https://loki.dev/blog/modern-tooling-part-1-uv/</id>
        
        <content type="html" xml:base="https://loki.dev/blog/modern-tooling-part-1-uv/">&lt;p&gt;Hey und willkommen zu meiner Serie über moderne Python-Werkzeuge: vier Teile
über die Werkzeuge selbst, dazu ein Bonusteil über KI-Agenten.
Ich schreibe das gerade, weil das Python-Ökosystem einen echten Umbruch erlebt.
Genau genommen sogar mehrere:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Das Python-Ökosystem wird zu großen Teilen durch Rust-basierte Werkzeuge
ersetzt. Das habe ich zu großen Teilen &lt;a rel=&quot;external&quot; href=&quot;https://astral.sh&quot;&gt;Astral&lt;/a&gt; zu verdanken.&lt;/li&gt;
&lt;li&gt;Immer größere Teile der Entwicklung werden von KI diverser Hersteller
erledigt, und die Kontrolle selbiger führt zu wachsenden Problemen.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Praktischerweise passen diese beiden Umbrüche perfekt zusammen.&lt;/p&gt;
&lt;p&gt;Mein Ziel ist also, erst diese neuen Rust-basierten Werkzeuge vorzustellen und
dann zu zeigen, wie sie in der KI-getriebenen Entwicklung helfen können. Ich
schaue mir vier+ dieser Werkzeuge an, und zwar nicht abstrakt, sondern anhand
eines realen FastAPI-Projekt-Templates: &lt;code&gt;uv&lt;/code&gt;, &lt;code&gt;ruff&lt;/code&gt;, &lt;code&gt;ty&lt;/code&gt; und &lt;code&gt;prek&lt;/code&gt; (+weitere
Mini-Tools).&lt;/p&gt;
&lt;p&gt;In &lt;strong&gt;Teil 1 (dieser hier)&lt;/strong&gt; geht es um das Fundament: &lt;strong&gt;&lt;code&gt;uv&lt;/code&gt;&lt;/strong&gt;.&lt;/p&gt;
&lt;h3 id=&quot;das-problem-dass-ich-sehe-funf-tools-fur-eine-aufgabe&quot;&gt;Das Problem, dass ich sehe: fünf Tools für eine Aufgabe&lt;/h3&gt;
&lt;p&gt;Wer bisher ein Python-Projekt sauber aufsetzen wollte, brauchte einen ganzen
Werkzeugkasten:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;pyenv&lt;/code&gt;, um die richtige Python-Version zu installieren&lt;/li&gt;
&lt;li&gt;&lt;code&gt;venv&lt;/code&gt; oder &lt;code&gt;virtualenv&lt;/code&gt; (&lt;code&gt;pipenv&lt;/code&gt; habe ich auch schon genutzt), um eine isolierte Umgebung zu erzeugen&lt;/li&gt;
&lt;li&gt;&lt;code&gt;pip&lt;/code&gt;, um Pakete zu installieren&lt;/li&gt;
&lt;li&gt;&lt;code&gt;pip-tools&lt;/code&gt;, &lt;code&gt;pip freeze&lt;/code&gt; (oder &lt;code&gt;poetry&lt;/code&gt;), um Abhängigkeiten zu locken&lt;/li&gt;
&lt;li&gt;dazu das &lt;code&gt;source .venv/bin/activate&lt;/code&gt;-Ritual (gerne auch automatisiert via zsh/bash script)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Jedes dieser Tools hat eine eigene Konfiguration (inklusive eigene
Konfigurationstypen), eigene Eigenheiten und eigene Fehlerquellen. Das Ergebnis
ist: &quot;Works on my machine&quot;. Gratis dazu: eine CI-Pipeline, die Minuten mit dem
Auflösen von Abhängigkeiten verbringt.&lt;/p&gt;
&lt;h3 id=&quot;die-losung-uv-ein-einzelnes-binary&quot;&gt;Die Lösung: uv, ein einzelnes Binary&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;uv&lt;/code&gt; ersetzt den kompletten Stack durch ein einziges Rust-Binary. Es ist
Paket-Installer, Environment-Manager, Dependency-Resolver und
Python-Versions-Manager in einem. Das Beste: es ist dabei um Größenordnungen schneller.&lt;/p&gt;
&lt;p&gt;Installation geht flott:&lt;/p&gt;
&lt;pre class=&quot;giallo z-code&quot; &gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-5&quot;&gt;curl&lt;/span&gt;&lt;span class=&quot;z-6&quot;&gt; -LsSf&lt;/span&gt;&lt;span class=&quot;z-11&quot;&gt; https://astral.sh/uv/install.sh&lt;/span&gt;&lt;span class=&quot;z-13&quot;&gt; |&lt;/span&gt;&lt;span class=&quot;z-5&quot;&gt; sh&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4&quot;&gt;# oder auf macOS:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-5&quot;&gt;brew&lt;/span&gt;&lt;span class=&quot;z-11&quot;&gt; install uv&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;em&gt;(Wie immer gilt: install script erst lesen oder sogar erst runterladen und
dann ausführen. Hier ist der Einfachheit halber nur der direkte Schritt aus der
Doku vermerkt)&lt;/em&gt;&lt;/p&gt;
&lt;h3 id=&quot;workflow&quot;&gt;Workflow&lt;/h3&gt;
&lt;p&gt;Danach reduziert sich der Python-Alltag auf drei Befehle:&lt;/p&gt;
&lt;pre class=&quot;giallo z-code&quot; &gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4&quot;&gt;# Abhängigkeiten aus dem Lockfile installieren (inkl. venv-Erstellung)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-5&quot;&gt;uv&lt;/span&gt;&lt;span class=&quot;z-11&quot;&gt; sync&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4&quot;&gt;# Ein Paket hinzufügen: aktualisiert pyproject.toml UND uv.lock&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-5&quot;&gt;uv&lt;/span&gt;&lt;span class=&quot;z-11&quot;&gt; add fastapi&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4&quot;&gt;# Beliebigen Befehl in der Projektumgebung ausführen&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-5&quot;&gt;uv&lt;/span&gt;&lt;span class=&quot;z-11&quot;&gt; run pytest&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Es gibt also kein &lt;code&gt;activate&lt;/code&gt; mehr. &lt;code&gt;uv run&lt;/code&gt; sorgt selbst dafür, dass der Befehl
in der richtigen Umgebung läuft und dass diese Umgebung mit dem Lockfile
übereinstimmt. Ist sie es nicht, synchronisiert &lt;code&gt;uv&lt;/code&gt; sie vorher automatisch.
Via &lt;code&gt;uv venv&lt;/code&gt; (optional mit &lt;code&gt;--python 314&lt;/code&gt;) kann man auch getrennt eine &lt;code&gt;venv&lt;/code&gt;
Umgebung erstellen. Alternativ kann man im Projekt eine &lt;code&gt;.python-version&lt;/code&gt;
Datei mit dem Inhalt &lt;code&gt;3.14&lt;/code&gt; anlegen. Dann lädt &lt;code&gt;uv&lt;/code&gt; den passenden Interpreter
bei Bedarf einfach herunter. &lt;code&gt;pyenv&lt;/code&gt; wird damit überflüssig.&lt;/p&gt;
&lt;h3 id=&quot;dependency-groups-statt-requirements-wildwuchs&quot;&gt;Dependency Groups statt requirements-Wildwuchs&lt;/h3&gt;
&lt;p&gt;Statt &lt;code&gt;requirements.txt&lt;/code&gt;, &lt;code&gt;requirements-dev.txt&lt;/code&gt; und &lt;code&gt;requirements-test.txt&lt;/code&gt;
definiert mein Template alles in der &lt;code&gt;pyproject.toml&lt;/code&gt;, mit dem Standard für
&lt;a rel=&quot;external&quot; href=&quot;https://peps.python.org/pep-0735/&quot;&gt;Dependency Groups (PEP 735)&lt;/a&gt;, inklusive
Gruppen, die andere Gruppen einbinden:&lt;/p&gt;
&lt;pre class=&quot;giallo z-code&quot; &gt;&lt;code data-lang=&quot;toml&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;[dependency-groups]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;dev = [&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-11&quot;&gt;  &amp;quot;prek==0.4.9&amp;quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  { include-group =&lt;/span&gt;&lt;span class=&quot;z-11&quot;&gt; &amp;quot;tests&amp;quot;&lt;/span&gt;&lt;span&gt; },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  { include-group =&lt;/span&gt;&lt;span class=&quot;z-11&quot;&gt; &amp;quot;lint&amp;quot;&lt;/span&gt;&lt;span&gt; },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;tests = [&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-11&quot;&gt;  &amp;quot;httpx==0.*&amp;quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-11&quot;&gt;  &amp;quot;pytest==9.*&amp;quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-11&quot;&gt;  &amp;quot;pytest-asyncio&amp;quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;lint = [&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-11&quot;&gt;  &amp;quot;ruff==0.*&amp;quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-11&quot;&gt;  &amp;quot;ty==0.*&amp;quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-11&quot;&gt;  &amp;quot;bandit==1.*&amp;quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-11&quot;&gt;  &amp;quot;import-linter&amp;gt;=2.11&amp;quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;uv sync&lt;/code&gt; installiert standardmäßig die &lt;code&gt;dev&lt;/code&gt;-Gruppe, &lt;code&gt;uv sync --no-dev&lt;/code&gt; lässt
sie weg. Perfekt für die Production Environment. Weitere Flags für die Gruppen
sind &lt;code&gt;--all-groups&lt;/code&gt; bzw. sehr spezifisch &lt;code&gt;--group lint|tests|dev&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id=&quot;reproduzierbarkeit-uv-lock&quot;&gt;Reproduzierbarkeit: uv.lock&lt;/h3&gt;
&lt;p&gt;Wichtig für stabile Deployments ist die &lt;code&gt;uv.lock&lt;/code&gt;: Sie hält jede Abhängigkeit
(inklusive Subabhängigkeiten) mit exakter Version und Hash fest,
plattformübergreifend. In CI und Docker erzwinge ich mit &lt;code&gt;--frozen&lt;/code&gt;, dass das
Lockfile aktuell ist. Weicht die &lt;code&gt;pyproject.toml&lt;/code&gt; davon ab, schlägt der Build
fehl, statt still etwas anderes zu installieren.&lt;/p&gt;
&lt;p&gt;So sieht das in einem beispielhaften Dockerfile meines Templates aus (Multi-Stage, mit Cache-Mount):&lt;/p&gt;
&lt;pre class=&quot;giallo z-code&quot; &gt;&lt;code data-lang=&quot;docker&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4&quot;&gt;# Bewusst kein alpine, da das gerade mit binary builds (z. B. psycopg2/etc.)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4&quot;&gt;# schonmal Mehrarbeit ist.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-13&quot;&gt;FROM&lt;/span&gt;&lt;span&gt; python:3.14-slim &lt;/span&gt;&lt;span class=&quot;z-13&quot;&gt;AS&lt;/span&gt;&lt;span&gt; builder&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-13&quot;&gt;ENV&lt;/span&gt;&lt;span&gt; UV_COMPILE_BYTECODE=1&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-13&quot;&gt;ENV&lt;/span&gt;&lt;span&gt; UV_LINK_MODE=copy&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4&quot;&gt;# Erster Schritt: uv haben. Es gibt aber auch fertige Docker Images!&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-13&quot;&gt;COPY&lt;/span&gt;&lt;span&gt; --from=ghcr.io/astral-sh/uv:latest /uv /bin/uv&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-13&quot;&gt;COPY&lt;/span&gt;&lt;span&gt; pyproject.toml uv.lock ./&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4&quot;&gt;# Erst nur Dependencies, dieser Layer wird gecached,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4&quot;&gt;# solange sich das Lockfile nicht ändert&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-13&quot;&gt;RUN&lt;/span&gt;&lt;span&gt; uv sync --frozen --no-dev --no-install-project&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4&quot;&gt;# Dann der sourcecode -&amp;gt; häufige änderungen&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-13&quot;&gt;COPY&lt;/span&gt;&lt;span&gt; src ./src&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4&quot;&gt;# Install mithilfe von cache (macht gerade bei CI einen gigantischen Unterschied)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-13&quot;&gt;RUN&lt;/span&gt;&lt;span&gt; --mount=type=cache,target=/root/.cache/uv \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    uv sync --frozen --no-dev&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Und in der GitHub-Actions-Pipeline übernimmt die offizielle Action Setup und
Caching in einem Schritt:&lt;/p&gt;
&lt;pre class=&quot;giallo z-code&quot; &gt;&lt;code data-lang=&quot;yaml&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;-&lt;/span&gt;&lt;span class=&quot;z-13&quot;&gt; name&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span class=&quot;z-11&quot;&gt; Install uv&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-13&quot;&gt;  uses&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span class=&quot;z-11&quot;&gt; astral-sh/setup-uv@v8.2.0&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-13&quot;&gt;  with&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-13&quot;&gt;    python-version&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span class=&quot;z-11&quot;&gt; &amp;quot;3.14&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-13&quot;&gt;    enable-cache&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span class=&quot;z-6&quot;&gt; true&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;-&lt;/span&gt;&lt;span class=&quot;z-13&quot;&gt; name&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span class=&quot;z-11&quot;&gt; Install dependencies&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-13&quot;&gt;  run&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span class=&quot;z-11&quot;&gt; uv sync --all-groups&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&quot;fazit&quot;&gt;Fazit&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;uv&lt;/code&gt; braucht weniger Konfiguration als der alte Stack und ist trotzdem
deutlich schneller. Es ist der Grund, warum alle folgenden Teile dieser Serie
mit &lt;code&gt;uv run ...&lt;/code&gt; beginnen. Damit werden Python-Entwicklung, CI, CD u. v. m.
schneller.&lt;/p&gt;
&lt;p&gt;Bonusfeature: &lt;a rel=&quot;external&quot; href=&quot;https://docs.astral.sh/uv/guides/scripts/#declaring-script-dependencies&quot;&gt;Scripts mit Inline-Dependencies&lt;/a&gt;
laufen lassen. Perfekt für DevOps und Ähnliches. :)&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;&lt;strong&gt;Die Serie:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Teil 1: uv (dieser Teil)&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://loki.dev/blog/modern-tooling-part-2-ruff/&quot;&gt;Teil 2: ruff&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://loki.dev/blog/modern-tooling-part-3-ty/&quot;&gt;Teil 3: ty&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://loki.dev/blog/modern-tooling-part-4-prek/&quot;&gt;Teil 4: prek&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://loki.dev/blog/modern-tooling-part-5-ai-harness/&quot;&gt;Teil 5: Korsett für KI-Agenten (Bonus)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content>
        
    </entry>
    <entry xml:lang="de">
        <title>Claude Code, Claw Code und das Ende der Geheimnisse?</title>
        <published>2026-05-04T12:00:00+00:00</published>
        <updated>2026-05-04T12:00:00+00:00</updated>
        
        <author>
          <name>Unknown</name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://loki.dev/blog/claude-code-leak-und-claw-code/"/>
        <id>https://loki.dev/blog/claude-code-leak-und-claw-code/</id>
        
        <content type="html" xml:base="https://loki.dev/blog/claude-code-leak-und-claw-code/">&lt;p&gt;Sorry für die lange Funkstille! Seit dem letzten Post ist ja doch einiges an Zeit ins Land gegangen. Ich hab hier noch einen ganzen Stapel an Entwürfen rumliegen (&lt;code&gt;.md.draft&lt;/code&gt;), aber wenn ich mir die so anschaue, müssen die wohl alle erst mal grundlegend überarbeitet werden, die Welt (und vor allem die KI-Welt) dreht sich einfach zu schnell ;).&lt;/p&gt;
&lt;p&gt;Aber jetzt treibt mich gerade genau diese KI rum und mir fehlt eine Stimme, die weder komplett vom Hype überrannt, noch in ihrem Loch lebt und die aktuelle Entwicklung ignoriert. Daher mache ich das jetzt einfach mal - sogar frei von KI geschrieben.&lt;/p&gt;
&lt;p&gt;Claude Code direkt als erstes, da es ziemlich deutlich die Beliebtheitsskala bei Entwicklern anführt. Es ist schon faszinierend (und ein bisschen ironisch), dass ausgerechnet ein Tool, das uns beim Programmieren helfen soll, seinen eigenen Quellcode veröffentlicht - und das sogar nachdem Mythic (das Ding, was so gut ist, dass man es vor der Öffentlichkeit verstecken muss) bereits im Einsatz war; oder weil? 🤔 Manche erinnern sich vielleicht noch an GPT5 wo ähnliches behaupted wurde.&lt;/p&gt;
&lt;h3 id=&quot;claw-code&quot;&gt;Claw Code&lt;/h3&gt;
&lt;p&gt;Wer sich das Ganze mal im Detail anschauen will (solange es noch online ist), sollte einen Blick auf &lt;a rel=&quot;external&quot; href=&quot;https://claw-code.codes/&quot;&gt;Claw Code&lt;/a&gt; werfen. Das Projekt hat den geleakten Code quasi &quot;deductiv&quot; aufbereitet und zugänglich gemacht. Es ist ein extrem spannendes Stück Softwarearchitektur, das zeigt, wie Anthropic die Interaktion zwischen LLM und lokaler Shell orchestriert.&lt;/p&gt;
&lt;h3 id=&quot;die-rechtliche-seite-dmca-whack-a-mole&quot;&gt;Die rechtliche Seite: DMCA-Whack-A-Mole&lt;/h3&gt;
&lt;p&gt;Natürlich findet Anthropic das Ganze weniger witzig. Es gab wohl schon über 8100 Löschanfragen. &lt;a rel=&quot;external&quot; href=&quot;https://www.heise.de/news/Claude-Code-Leck-8100-Loeschantraege-und-die-Geburtsstunde-von-Claw-Code-11279601.html&quot;&gt;Heise hat dazu einen sehr guten Artikel geschrieben&lt;/a&gt;, der die rechtlichen Aspekte beleuchtet. Es ist die klassische &quot;Geburtsstunde&quot; eines Tools aus einem Leak heraus, und der verzweifelte Versuch, die Katze wieder in den Sack zu kriegen.&lt;/p&gt;
&lt;p&gt;Ich finde das ziemlich bezeichnend für den current State der KI:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Die Tools werden immer mächtiger, aber auch komplexer.&lt;/li&gt;
&lt;li&gt;Der Code (und die Bugs) entstehen schneller und kriegen immer weniger echte Reviews&lt;/li&gt;
&lt;li&gt;&quot;Security by Obscurity&quot; funktioniert mit KI noch weniger als vorher&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Gerade überlege ich noch, ob ich dazu mal einen tieferen Artikel schreibe, wo ich mich speziell um &lt;strong&gt;KI Guardrails&lt;/strong&gt;, also eine Art Sicherheitskorsett für KI kümmere. Ich habe das Gefühl, dass dies vielen noch fehlt obwohl es echt eine schöne tief hängende Frucht ist.&lt;/p&gt;
</content>
        
    </entry>
    <entry xml:lang="de">
        <title>Immutability / Const vs Let</title>
        <published>2024-12-06T09:34:00+00:00</published>
        <updated>2024-12-18T07:34:00+00:00</updated>
        
        <author>
          <name>Unknown</name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://loki.dev/blog/const-immutability/"/>
        <id>https://loki.dev/blog/const-immutability/</id>
        
        <content type="html" xml:base="https://loki.dev/blog/const-immutability/">&lt;h2 id=&quot;tl-dr&quot;&gt;tl;dr&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;const&lt;/code&gt; bevorzugen statt &lt;code&gt;let&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Variablen so oft wie möglich unveränderlich halten&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Ich erkläre hier, warum Immutability so nützlich ist und wie man sie in TypeScript einsetzt, ähnliche Konzepte gelten aber auch in anderen Sprachen.&lt;/p&gt;
&lt;h2 id=&quot;was-ist-immutability&quot;&gt;Was ist Immutability?&lt;/h2&gt;
&lt;p&gt;Der Name beschreibt es eigentlich schon: Dinge unveränderlich machen. In TypeScript sind alle primitiven Datentypen bereits unveränderlich!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Beispiel:&lt;/strong&gt;&lt;/p&gt;
&lt;pre class=&quot;giallo z-code&quot; &gt;&lt;code data-lang=&quot;typescript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-1 z-i&quot;&gt;let&lt;/span&gt;&lt;span&gt; a&lt;/span&gt;&lt;span class=&quot;z-13&quot;&gt; =&lt;/span&gt;&lt;span class=&quot;z-6&quot;&gt; 5&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-1 z-i&quot;&gt;let&lt;/span&gt;&lt;span&gt; b&lt;/span&gt;&lt;span class=&quot;z-13&quot;&gt; =&lt;/span&gt;&lt;span&gt; a;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;a&lt;/span&gt;&lt;span class=&quot;z-13&quot;&gt; =&lt;/span&gt;&lt;span class=&quot;z-6&quot;&gt; 123&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4&quot;&gt;// 5 existiert noch im Speicher&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;b&lt;/span&gt;&lt;span class=&quot;z-13&quot;&gt; =&lt;/span&gt;&lt;span class=&quot;z-6&quot;&gt; 42&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4&quot;&gt;// 5 wird bald garbage collected&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Kleiner Hinweis für zukünftige Inhalte: Immutability ist ein zentrales Konzept in der funktionalen Programmierung ;).&lt;/p&gt;
&lt;p&gt;Tiefer einsteigen mit einem Beispiel:&lt;/p&gt;
&lt;pre class=&quot;giallo z-code&quot; &gt;&lt;code data-lang=&quot;typescript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-1 z-i&quot;&gt;let&lt;/span&gt;&lt;span&gt; helloWorldString&lt;/span&gt;&lt;span class=&quot;z-13&quot;&gt; =&lt;/span&gt;&lt;span class=&quot;z-11&quot;&gt; &amp;quot;Hello World!&amp;quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt; // unveränderlich&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;helloWorldString&lt;/span&gt;&lt;span class=&quot;z-13&quot;&gt; =&lt;/span&gt;&lt;span class=&quot;z-11&quot;&gt; &amp;quot;Hello Moon!&amp;quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt; // kein Fehler, aber der Variablenname stimmt nicht mehr!&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4&quot;&gt;// vs:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-1 z-i&quot;&gt;const&lt;/span&gt;&lt;span&gt; helloWorldString&lt;/span&gt;&lt;span class=&quot;z-13&quot;&gt; =&lt;/span&gt;&lt;span class=&quot;z-11&quot;&gt; &amp;quot;Hello World!&amp;quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt; // unveränderlich&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;helloWorldString&lt;/span&gt;&lt;span class=&quot;z-13&quot;&gt; =&lt;/span&gt;&lt;span class=&quot;z-11&quot;&gt; &amp;quot;Hello Moon!&amp;quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt; // Fehler! Wir können uns auf den Inhalt verlassen!&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Im oberen Beispiel ändert sich der Inhalt der Variable und es wird schwerer vorherzusagen, was drin steckt.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;„Aber was ist mit Zählervariablen? Ich muss doch ständig zählen!&quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Richtig. Aufgedröselt:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Die Faustregel lautet:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;veränderliche Variablen &lt;strong&gt;möglichst&lt;/strong&gt; vermeiden&lt;/li&gt;
&lt;li&gt;wenn veränderliche Variablen die Lesbarkeit verbessern oder die einzige sinnvolle Option sind (sehr selten): &lt;strong&gt;dann verwenden&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Mit „selten&quot; meine ich, dass TypeScript/JavaScript uns in vielen Fällen einen Weg bietet, Zählervariablen zu vermeiden:&lt;/p&gt;
&lt;pre class=&quot;giallo z-code&quot; &gt;&lt;code data-lang=&quot;typescript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-1 z-i&quot;&gt;let&lt;/span&gt;&lt;span&gt; uppercaseNames&lt;/span&gt;&lt;span class=&quot;z-13&quot;&gt; =&lt;/span&gt;&lt;span&gt; [];&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-13&quot;&gt;for&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span class=&quot;z-1 z-i&quot;&gt;let&lt;/span&gt;&lt;span&gt; name&lt;/span&gt;&lt;span class=&quot;z-13&quot;&gt; of&lt;/span&gt;&lt;span&gt; [&lt;/span&gt;&lt;span class=&quot;z-11&quot;&gt;&amp;quot;alice&amp;quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span class=&quot;z-11&quot;&gt; &amp;quot;bob&amp;quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span class=&quot;z-11&quot;&gt; &amp;quot;charlie&amp;quot;&lt;/span&gt;&lt;span&gt;]) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  uppercaseNames.&lt;/span&gt;&lt;span class=&quot;z-5&quot;&gt;push&lt;/span&gt;&lt;span&gt;(name.&lt;/span&gt;&lt;span class=&quot;z-5&quot;&gt;toUpperCase&lt;/span&gt;&lt;span&gt;());&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4&quot;&gt;// Funktionaler Ansatz&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-1 z-i&quot;&gt;const&lt;/span&gt;&lt;span&gt; uppercaseNames&lt;/span&gt;&lt;span class=&quot;z-13&quot;&gt; =&lt;/span&gt;&lt;span&gt; [&lt;/span&gt;&lt;span class=&quot;z-11&quot;&gt;&amp;quot;alice&amp;quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span class=&quot;z-11&quot;&gt; &amp;quot;bob&amp;quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span class=&quot;z-11&quot;&gt; &amp;quot;charlie&amp;quot;&lt;/span&gt;&lt;span&gt;].&lt;/span&gt;&lt;span class=&quot;z-5&quot;&gt;map&lt;/span&gt;&lt;span&gt;((&lt;/span&gt;&lt;span class=&quot;z-14 z-i&quot;&gt;name&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span class=&quot;z-1 z-i&quot;&gt; =&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  name.&lt;/span&gt;&lt;span class=&quot;z-5&quot;&gt;toUpperCase&lt;/span&gt;&lt;span&gt;(),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Dieses Beispiel ist trivial, aber in jedem Fall: Wir WISSEN, dass &lt;code&gt;uppercaseNames&lt;/code&gt; genau das enthält, was es sagt, sobald es definiert ist!&lt;/p&gt;
&lt;h2 id=&quot;warum&quot;&gt;Warum&lt;/h2&gt;
&lt;p&gt;Immutability bedeutet nicht, alles konsequent durchzuziehen, und veränderliche Variablen sind auch nicht per se schlecht, aber sie eliminieren einige Fehlerquellen beim Programmieren und Debuggen.&lt;/p&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https://biomejs.dev&quot;&gt;Biome&lt;/a&gt; hat einen Lint &lt;code&gt;lint/style/useConst&lt;/code&gt;, der zwingt, &lt;code&gt;const&lt;/code&gt; zu verwenden, wenn eine Variable nie neu zugewiesen wird. Ich würde aber noch einen Schritt weiter gehen und &lt;code&gt;let&lt;/code&gt; so oft wie möglich durch &lt;code&gt;const&lt;/code&gt; ersetzen.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Folgendes Szenario:&lt;/strong&gt;&lt;/p&gt;
&lt;pre class=&quot;giallo z-code&quot; &gt;&lt;code data-lang=&quot;typescript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-1 z-i&quot;&gt;const&lt;/span&gt;&lt;span class=&quot;z-5&quot;&gt; mutateVar&lt;/span&gt;&lt;span class=&quot;z-13&quot;&gt; =&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span class=&quot;z-14 z-i&quot;&gt;innerA&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span class=&quot;z-1 z-i&quot;&gt; =&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  innerA&lt;/span&gt;&lt;span class=&quot;z-13&quot;&gt; =&lt;/span&gt;&lt;span class=&quot;z-6&quot;&gt; 9&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-13&quot;&gt;  return&lt;/span&gt;&lt;span class=&quot;z-6&quot;&gt; 12&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;};&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-1 z-i&quot;&gt;let&lt;/span&gt;&lt;span&gt; outerA&lt;/span&gt;&lt;span class=&quot;z-13&quot;&gt; =&lt;/span&gt;&lt;span class=&quot;z-6&quot;&gt; 123&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-1 z-i&quot;&gt;const&lt;/span&gt;&lt;span&gt; result&lt;/span&gt;&lt;span class=&quot;z-13&quot;&gt; =&lt;/span&gt;&lt;span class=&quot;z-5&quot;&gt; mutateVar&lt;/span&gt;&lt;span&gt;(outerA);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4&quot;&gt;// keine Änderung, da elementare Typen per Wert übergeben werden&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;console.&lt;/span&gt;&lt;span class=&quot;z-5&quot;&gt;log&lt;/span&gt;&lt;span&gt;({ result, outerA });&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4&quot;&gt;// Aber:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-1 z-i&quot;&gt;const&lt;/span&gt;&lt;span class=&quot;z-5&quot;&gt; mutateObj&lt;/span&gt;&lt;span class=&quot;z-13&quot;&gt; =&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span class=&quot;z-14 z-i&quot;&gt;obj&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span class=&quot;z-1 z-i&quot;&gt; =&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4&quot;&gt;  // Seiteneffekt, den wir beim Lesen des Funktionsnamens nicht erwarten:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  obj.x&lt;/span&gt;&lt;span class=&quot;z-13&quot;&gt; =&lt;/span&gt;&lt;span class=&quot;z-6&quot;&gt; 123&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-13&quot;&gt;  return&lt;/span&gt;&lt;span&gt; obj;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;};&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-1 z-i&quot;&gt;const&lt;/span&gt;&lt;span&gt; originalObject&lt;/span&gt;&lt;span class=&quot;z-13&quot;&gt; =&lt;/span&gt;&lt;span&gt; { a:&lt;/span&gt;&lt;span class=&quot;z-6&quot;&gt; 42&lt;/span&gt;&lt;span&gt; };&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-1 z-i&quot;&gt;const&lt;/span&gt;&lt;span&gt; mutatedObject&lt;/span&gt;&lt;span class=&quot;z-13&quot;&gt; =&lt;/span&gt;&lt;span class=&quot;z-5&quot;&gt; mutateObj&lt;/span&gt;&lt;span&gt;(originalObject);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;console.&lt;/span&gt;&lt;span class=&quot;z-5&quot;&gt;log&lt;/span&gt;&lt;span&gt;({ originalObject, mutatedObject });&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Wir erwarten, dass &lt;code&gt;originalObject&lt;/code&gt; noch gleich ist und &lt;code&gt;mutatedObject&lt;/code&gt; den neuen Wert enthält. Wenn wir uns angewöhnen, bestehende Objekte nicht zu verändern und &lt;code&gt;const&lt;/code&gt; zu verwenden, werden wir nie von Verhalten &lt;strong&gt;innerhalb&lt;/strong&gt; einer Funktion überrascht.&lt;/p&gt;
&lt;p&gt;Funktionen, die Parameter nicht mutieren und ausschließlich von ihren Parametern abhängen, heißen &lt;strong&gt;pure functions&lt;/strong&gt;. Ein Konzept mit vielen Vorteilen gegenüber &lt;strong&gt;unreinen&lt;/strong&gt; Funktionen, dazu mehr im nächsten Artikel.&lt;/p&gt;
&lt;h2 id=&quot;aber-const-objekte-sind-doch-nicht-unveranderlich&quot;&gt;Aber const-Objekte sind doch NICHT unveränderlich&lt;/h2&gt;
&lt;p&gt;Stimmt. Besonders wegen JavaScript. Glücklicherweise haben wir in TypeScript mächtige Werkzeuge: &lt;code&gt;Readonly&lt;/code&gt;, &lt;code&gt;readonly&lt;/code&gt; und &lt;code&gt;as const&lt;/code&gt;.&lt;/p&gt;
&lt;pre class=&quot;giallo z-code&quot; &gt;&lt;code data-lang=&quot;typescript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4&quot;&gt;// Readonly&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-1 z-i&quot;&gt;type&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span class=&quot;z-5 z-u&quot;&gt;Tree&lt;/span&gt;&lt;span class=&quot;z-13&quot;&gt; =&lt;/span&gt;&lt;span&gt; { name&lt;/span&gt;&lt;span class=&quot;z-13&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;z-1 z-i&quot;&gt; string&lt;/span&gt;&lt;span&gt; };&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-1 z-i&quot;&gt;const&lt;/span&gt;&lt;span&gt; ahorn&lt;/span&gt;&lt;span class=&quot;z-13&quot;&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span class=&quot;z-5 z-u&quot;&gt;Readonly&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;z-5 z-u&quot;&gt;Tree&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;z-13&quot;&gt; =&lt;/span&gt;&lt;span&gt; { name:&lt;/span&gt;&lt;span class=&quot;z-11&quot;&gt; &amp;quot;ahorn&amp;quot;&lt;/span&gt;&lt;span&gt; };&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;ahorn.name&lt;/span&gt;&lt;span class=&quot;z-13&quot;&gt; =&lt;/span&gt;&lt;span class=&quot;z-11&quot;&gt; &amp;quot;bhorn&amp;quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt; // TypeScript-Fehler&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4&quot;&gt;// As const&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-1 z-i&quot;&gt;const&lt;/span&gt;&lt;span&gt; ahorn&lt;/span&gt;&lt;span class=&quot;z-13&quot;&gt; =&lt;/span&gt;&lt;span&gt; { name:&lt;/span&gt;&lt;span class=&quot;z-11&quot;&gt; &amp;quot;ahorn&amp;quot;&lt;/span&gt;&lt;span&gt; }&lt;/span&gt;&lt;span class=&quot;z-13&quot;&gt; as const&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;ahorn.name&lt;/span&gt;&lt;span class=&quot;z-13&quot;&gt; =&lt;/span&gt;&lt;span class=&quot;z-11&quot;&gt; &amp;quot;bhorn&amp;quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt; // TypeScript-Fehler&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4&quot;&gt;// readonly&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-1 z-i&quot;&gt;class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span class=&quot;z-5 z-u&quot;&gt;Tree&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-1 z-i&quot;&gt;  constructor&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span class=&quot;z-13&quot;&gt;readonly&lt;/span&gt;&lt;span class=&quot;z-14 z-i&quot;&gt; name&lt;/span&gt;&lt;span class=&quot;z-13&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;z-1 z-i&quot;&gt; string&lt;/span&gt;&lt;span&gt;) {}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-1 z-i&quot;&gt;const&lt;/span&gt;&lt;span&gt; t&lt;/span&gt;&lt;span class=&quot;z-13&quot;&gt; = new&lt;/span&gt;&lt;span class=&quot;z-5&quot;&gt; Tree&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span class=&quot;z-11&quot;&gt;&amp;quot;ahorn&amp;quot;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;t.name&lt;/span&gt;&lt;span class=&quot;z-13&quot;&gt; =&lt;/span&gt;&lt;span class=&quot;z-11&quot;&gt; &amp;quot;asdf&amp;quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt; // TypeScript-Fehler&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&quot;was-ist-mit-string-buildern&quot;&gt;Was ist mit String-Buildern?&lt;/h2&gt;
&lt;p&gt;Ich bevorzuge persönlich:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Den Aufbau eines Strings in eine Funktion auszulagern&lt;/li&gt;
&lt;li&gt;Ein Template am Ende zu haben, in das die verschiedenen Teile einfließen&lt;/li&gt;
&lt;/ol&gt;
&lt;pre class=&quot;giallo z-code&quot; &gt;&lt;code data-lang=&quot;typescript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4&quot;&gt;/** Ansatz der meisten Leute */&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-1 z-i&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;z-5&quot;&gt; greetCustomer&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span class=&quot;z-14 z-i&quot;&gt;customer&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-1 z-i&quot;&gt;  let&lt;/span&gt;&lt;span&gt; message&lt;/span&gt;&lt;span class=&quot;z-13&quot;&gt; =&lt;/span&gt;&lt;span class=&quot;z-11&quot;&gt; &amp;quot;&amp;quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-13&quot;&gt;  if&lt;/span&gt;&lt;span&gt; (customer.age&lt;/span&gt;&lt;span class=&quot;z-13&quot;&gt; &amp;gt;&lt;/span&gt;&lt;span class=&quot;z-6&quot;&gt; 15&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    message&lt;/span&gt;&lt;span class=&quot;z-13&quot;&gt; +=&lt;/span&gt;&lt;span class=&quot;z-11&quot;&gt; &amp;quot;Guten Tag,&lt;/span&gt;&lt;span class=&quot;z-6&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;z-11&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  }&lt;/span&gt;&lt;span class=&quot;z-13&quot;&gt; else&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    message&lt;/span&gt;&lt;span class=&quot;z-13&quot;&gt; +=&lt;/span&gt;&lt;span class=&quot;z-11&quot;&gt; &amp;quot;Hey,&lt;/span&gt;&lt;span class=&quot;z-6&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;z-11&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-13&quot;&gt;  if&lt;/span&gt;&lt;span&gt; (customer.hasEaten) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    message&lt;/span&gt;&lt;span class=&quot;z-13&quot;&gt; +=&lt;/span&gt;&lt;span class=&quot;z-11&quot;&gt; &amp;quot;Darf ich Ihnen etwas zu trinken anbieten?&amp;quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  }&lt;/span&gt;&lt;span class=&quot;z-13&quot;&gt; else&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    message&lt;/span&gt;&lt;span class=&quot;z-13&quot;&gt; +=&lt;/span&gt;&lt;span class=&quot;z-11&quot;&gt; &amp;quot;Darf ich Ihnen unsere Mittagskarte zeigen?&amp;quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  console.&lt;/span&gt;&lt;span class=&quot;z-5&quot;&gt;log&lt;/span&gt;&lt;span&gt;(message);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4&quot;&gt;// vs. mein Ansatz&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-1 z-i&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;z-5&quot;&gt; buildGreeting&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span class=&quot;z-14 z-i&quot;&gt;customer&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span class=&quot;z-13&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;z-1 z-i&quot;&gt; string&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-1 z-i&quot;&gt;  const&lt;/span&gt;&lt;span&gt; greet&lt;/span&gt;&lt;span class=&quot;z-13&quot;&gt; =&lt;/span&gt;&lt;span&gt; customer.age&lt;/span&gt;&lt;span class=&quot;z-13&quot;&gt; &amp;gt;&lt;/span&gt;&lt;span class=&quot;z-6&quot;&gt; 15&lt;/span&gt;&lt;span class=&quot;z-13&quot;&gt; ?&lt;/span&gt;&lt;span class=&quot;z-11&quot;&gt; &amp;quot;Guten Tag&amp;quot;&lt;/span&gt;&lt;span class=&quot;z-13&quot;&gt; :&lt;/span&gt;&lt;span class=&quot;z-11&quot;&gt; &amp;quot;Hey&amp;quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-1 z-i&quot;&gt;  const&lt;/span&gt;&lt;span&gt; offer&lt;/span&gt;&lt;span class=&quot;z-13&quot;&gt; =&lt;/span&gt;&lt;span&gt; customer.hasEaten&lt;/span&gt;&lt;span class=&quot;z-13&quot;&gt; ?&lt;/span&gt;&lt;span class=&quot;z-11&quot;&gt; &amp;quot;etwas zu trinken&amp;quot;&lt;/span&gt;&lt;span class=&quot;z-13&quot;&gt; :&lt;/span&gt;&lt;span class=&quot;z-11&quot;&gt; &amp;quot;unsere Mittagskarte&amp;quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-13&quot;&gt;  return&lt;/span&gt;&lt;span class=&quot;z-11&quot;&gt; `&lt;/span&gt;&lt;span class=&quot;z-13&quot;&gt;${&lt;/span&gt;&lt;span&gt;greet&lt;/span&gt;&lt;span class=&quot;z-13&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;z-11&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;z-6&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;z-11&quot;&gt;Darf ich Ihnen &lt;/span&gt;&lt;span class=&quot;z-13&quot;&gt;${&lt;/span&gt;&lt;span&gt;offer&lt;/span&gt;&lt;span class=&quot;z-13&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;z-11&quot;&gt; anbieten?`&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-1 z-i&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;z-5&quot;&gt; greetCustomer&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span class=&quot;z-14 z-i&quot;&gt;customer&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-1 z-i&quot;&gt;  const&lt;/span&gt;&lt;span&gt; greeting&lt;/span&gt;&lt;span class=&quot;z-13&quot;&gt; =&lt;/span&gt;&lt;span class=&quot;z-5&quot;&gt; buildGreeting&lt;/span&gt;&lt;span&gt;(customer);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  console.&lt;/span&gt;&lt;span class=&quot;z-5&quot;&gt;log&lt;/span&gt;&lt;span&gt;(greeting);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Das ist besser, weil es:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;die Komplexität reduziert und die Struktur des Endtextes klarer macht&lt;/li&gt;
&lt;li&gt;die Einrückungstiefe verringert, auch ein Thema für einen eigenen Artikel!&lt;/li&gt;
&lt;li&gt;die Logik in eine pure function auslagert, während die Funktion mit Seiteneffekten nur diese nutzt&lt;/li&gt;
&lt;li&gt;&lt;code&gt;buildGreeting&lt;/code&gt; damit einfacher testbar macht, da wir nicht auf Console-Output angewiesen sind&lt;/li&gt;
&lt;/ul&gt;
</content>
        
    </entry>
    <entry xml:lang="de">
        <title>Linter &amp; Stylecheck</title>
        <published>2024-12-02T09:00:00+00:00</published>
        <updated>2024-12-03T06:32:23+00:00</updated>
        
        <author>
          <name>Unknown</name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://loki.dev/blog/linter-stylecheck/"/>
        <id>https://loki.dev/blog/linter-stylecheck/</id>
        
        <content type="html" xml:base="https://loki.dev/blog/linter-stylecheck/">&lt;h2 id=&quot;stylecheck&quot;&gt;Stylecheck&lt;/h2&gt;
&lt;p&gt;Fangen wir mit dem einfacheren Thema an: Stylechecks. Im Kern geht es darum, den gleichen Stil über die gesamte Anwendung beizubehalten. Welcher Stil das ist, ist dabei gar nicht so wichtig: Hauptsache, er ist überall gleich!&lt;/p&gt;
&lt;h4 id=&quot;warum&quot;&gt;Warum?&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Pattern-Matching:&lt;/strong&gt; Unabhängig vom Erfahrungsstand ist es einfacher, Muster zu erkennen, wenn sie immer gleich aufgebaut sind.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Git-Diffs:&lt;/strong&gt; Unterschiedliche Stile führen zu einem ständigen Hin-und-Her in den Diffs, wenn verschiedene Styles aufeinandertreffen.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Werkzeuge:&lt;/strong&gt; Ein Tool, das Code automatisch formatiert und Regeln direkt anwendet, spart enorm viel Zeit!&lt;/li&gt;
&lt;/ol&gt;
&lt;h4 id=&quot;tools&quot;&gt;Tools&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https://prettier.io&quot;&gt;prettier&lt;/a&gt; ist das bekanntere Werkzeug dafür&lt;/li&gt;
&lt;li&gt;Ich bevorzuge aber &lt;a rel=&quot;external&quot; href=&quot;https://biomejs.dev/formatter&quot;&gt;biome&lt;/a&gt;, da es schneller ist und auch &lt;strong&gt;Linting&lt;/strong&gt; übernehmen kann
&lt;ul&gt;
&lt;li&gt;Es ist zu 97 % kompatibel mit Prettier (gleiche Regeln), die Unterschiede &lt;a rel=&quot;external&quot; href=&quot;https://github.com/biomejs/biome/blob/main/crates/biome_js_formatter/report.md&quot;&gt;sind nachvollziehbar&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Es kann formatieren: JavaScript, TypeScript, JSX, TSX, JSON, CSS und GraphQL&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;beispiele&quot;&gt;Beispiele&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Trivial&lt;/strong&gt;: einheitliche Anführungszeichen &lt;code&gt;&#39;&lt;/code&gt; vs &lt;code&gt;&quot;&lt;/code&gt; oder konsistente Einrückung&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Lesbarkeit&lt;/strong&gt;: Lange Import-Zeilen auf mehrere Zeilen aufteilen&lt;/li&gt;
&lt;/ul&gt;
&lt;h6 id=&quot;trivial-leerzeichen&quot;&gt;Trivial: Leerzeichen&lt;/h6&gt;
&lt;pre class=&quot;giallo z-code&quot; &gt;&lt;code data-lang=&quot;typescript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4&quot;&gt;// VORHER&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;@&lt;/span&gt;&lt;span class=&quot;z-5&quot;&gt;Injectable&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-13&quot;&gt;export&lt;/span&gt;&lt;span class=&quot;z-1 z-i&quot;&gt; class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span class=&quot;z-5 z-u&quot;&gt;JwtService&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-5&quot;&gt;    decodeToken&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span class=&quot;z-14 z-i&quot;&gt;token&lt;/span&gt;&lt;span class=&quot;z-13&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;z-1 z-i&quot;&gt; string&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span class=&quot;z-13&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;z-1 z-i&quot;&gt; any&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-13&quot;&gt;        return&lt;/span&gt;&lt;span&gt; jwt.&lt;/span&gt;&lt;span class=&quot;z-5&quot;&gt;decode&lt;/span&gt;&lt;span&gt;(token);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4&quot;&gt;// NACHHER&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;@&lt;/span&gt;&lt;span class=&quot;z-5&quot;&gt;Injectable&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-13&quot;&gt;export&lt;/span&gt;&lt;span class=&quot;z-1 z-i&quot;&gt; class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span class=&quot;z-5 z-u&quot;&gt;JwtService&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-5&quot;&gt;    decodeToken&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span class=&quot;z-14 z-i&quot;&gt;token&lt;/span&gt;&lt;span class=&quot;z-13&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;z-1 z-i&quot;&gt; string&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span class=&quot;z-13&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;z-1 z-i&quot;&gt; any&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-13&quot;&gt;        return&lt;/span&gt;&lt;span&gt; jwt.&lt;/span&gt;&lt;span class=&quot;z-5&quot;&gt;decode&lt;/span&gt;&lt;span&gt;(token);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;oder Tabs vs. Spaces:&lt;/p&gt;
&lt;pre class=&quot;giallo z-code&quot; &gt;&lt;code data-lang=&quot;typescript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4&quot;&gt;// VORHER: 2 Spaces&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-5&quot;&gt;describe&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span class=&quot;z-11&quot;&gt;&amp;#39;ReportsController&amp;#39;&lt;/span&gt;&lt;span&gt;, ()&lt;/span&gt;&lt;span class=&quot;z-1 z-i&quot;&gt; =&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-1 z-i&quot;&gt;  let&lt;/span&gt;&lt;span&gt; controller&lt;/span&gt;&lt;span class=&quot;z-13&quot;&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span class=&quot;z-5 z-u&quot;&gt;ReportsController&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-5&quot;&gt;  beforeEach&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span class=&quot;z-13&quot;&gt;async&lt;/span&gt;&lt;span&gt; ()&lt;/span&gt;&lt;span class=&quot;z-1 z-i&quot;&gt; =&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4&quot;&gt;// NACHHER: Tabs&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-5&quot;&gt;describe&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span class=&quot;z-11&quot;&gt;&amp;#39;ReportsController&amp;#39;&lt;/span&gt;&lt;span&gt;, ()&lt;/span&gt;&lt;span class=&quot;z-1 z-i&quot;&gt; =&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-1 z-i&quot;&gt;	let&lt;/span&gt;&lt;span&gt; controller&lt;/span&gt;&lt;span class=&quot;z-13&quot;&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span class=&quot;z-5 z-u&quot;&gt;ReportsController&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-5&quot;&gt;	beforeEach&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span class=&quot;z-13&quot;&gt;async&lt;/span&gt;&lt;span&gt; ()&lt;/span&gt;&lt;span class=&quot;z-1 z-i&quot;&gt; =&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;* Ich persönlich bevorzuge Spaces, aber &lt;strong&gt;Konvention über Sympathie!&lt;/strong&gt;&lt;/p&gt;
&lt;h6 id=&quot;trivial-zeilenumbruche&quot;&gt;Trivial: Zeilenumbrüche&lt;/h6&gt;
&lt;pre class=&quot;giallo z-code&quot; &gt;&lt;code data-lang=&quot;typescript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4&quot;&gt;// VORHER&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;@&lt;/span&gt;&lt;span class=&quot;z-5&quot;&gt;Module&lt;/span&gt;&lt;span&gt;({&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  controllers: [IntegrationController],&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  providers: [IntegrationService, PrismaService, ScrapingService, CustomerIoService, JwtService]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;})&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4&quot;&gt;// NACHHER&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;@&lt;/span&gt;&lt;span class=&quot;z-5&quot;&gt;Module&lt;/span&gt;&lt;span&gt;({&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	controllers: [IntegrationController],&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	providers: [&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;		IntegrationService,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;		PrismaService,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;		ScrapingService,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;		CustomerIoService,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;		JwtService,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	],&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;})&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Es gibt auch den Analyzer, der Imports in einer vorhersehbaren Reihenfolge sortiert. Das ist optional, aber gibt jedem Datei-Header die gleiche Struktur. Details zur Reihenfolge gibt es &lt;a rel=&quot;external&quot; href=&quot;https://biomejs.dev/analyzer/import-sorting/&quot;&gt;hier&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;linting&quot;&gt;Linting&lt;/h2&gt;
&lt;p&gt;Linting gibt uns einen Vorsprung, bevor Kunden Bugs finden. Jeder Linter hat natürlich eine kleine False-Positive-Rate, aber das Regelwerk schließt ganze Klassen von Fehlern aus, bevor sie überhaupt passieren!&lt;/p&gt;
&lt;p&gt;Das vollständige Regelwerk von Biome findet sich &lt;a rel=&quot;external&quot; href=&quot;https://biomejs.dev/linter/rules/&quot;&gt;hier&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Es hat triviale Checks wie &lt;a rel=&quot;external&quot; href=&quot;https://biomejs.dev/linter/rules/no-distracting-elements/&quot;&gt;no distracting elements&lt;/a&gt;
(verbietet &lt;a rel=&quot;external&quot; href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/marquee&quot;&gt;marquee&lt;/a&gt; und &lt;a rel=&quot;external&quot; href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/blink&quot;&gt;blink&lt;/a&gt;), aber auch deutlich fortgeschrittenere Regeln.&lt;/p&gt;
&lt;p&gt;Meine Lieblingskategorie ist &lt;a rel=&quot;external&quot; href=&quot;https://biomejs.dev/linter/rules/#nursery&quot;&gt;nursery&lt;/a&gt;, die veraltete JS-Features enthält, die durch modernere Syntax ersetzt wurden.&lt;/p&gt;
&lt;h4 id=&quot;linter-warnungen-beispiele&quot;&gt;Linter-Warnungen: Beispiele&lt;/h4&gt;
&lt;pre class=&quot;giallo z-code&quot; &gt;&lt;code data-lang=&quot;typescript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4&quot;&gt;// 1. no banned types&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;code&lt;/span&gt;&lt;span class=&quot;z-13&quot;&gt;-&lt;/span&gt;&lt;span&gt;block.ts:&lt;/span&gt;&lt;span class=&quot;z-6&quot;&gt;1&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span class=&quot;z-6&quot;&gt;10&lt;/span&gt;&lt;span&gt; lint&lt;/span&gt;&lt;span class=&quot;z-13&quot;&gt;/&lt;/span&gt;&lt;span&gt;complexity&lt;/span&gt;&lt;span class=&quot;z-13&quot;&gt;/&lt;/span&gt;&lt;span&gt;noBannedTypes ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  ✖ Don&lt;/span&gt;&lt;span class=&quot;z-11&quot;&gt;&amp;#39;t use &amp;#39;&lt;/span&gt;&lt;span&gt;{}&lt;/span&gt;&lt;span class=&quot;z-11&quot;&gt;&amp;#39; as a type&lt;/span&gt;&lt;span class=&quot;z-12&quot;&gt;.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-13&quot;&gt;  &amp;gt;&lt;/span&gt;&lt;span class=&quot;z-6&quot;&gt; 1&lt;/span&gt;&lt;span&gt; │ &lt;/span&gt;&lt;span class=&quot;z-1 z-i&quot;&gt;const&lt;/span&gt;&lt;span&gt; n&lt;/span&gt;&lt;span class=&quot;z-13&quot;&gt;:&lt;/span&gt;&lt;span&gt; {}&lt;/span&gt;&lt;span class=&quot;z-13&quot;&gt; =&lt;/span&gt;&lt;span class=&quot;z-6&quot;&gt; 0&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      │          &lt;/span&gt;&lt;span class=&quot;z-13&quot;&gt;^^&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4&quot;&gt;// 2. complexity&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;code&lt;/span&gt;&lt;span class=&quot;z-13&quot;&gt;-&lt;/span&gt;&lt;span&gt;block.js:&lt;/span&gt;&lt;span class=&quot;z-6&quot;&gt;1&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span class=&quot;z-6&quot;&gt;10&lt;/span&gt;&lt;span&gt; lint&lt;/span&gt;&lt;span class=&quot;z-13&quot;&gt;/&lt;/span&gt;&lt;span&gt;complexity&lt;/span&gt;&lt;span class=&quot;z-13&quot;&gt;/&lt;/span&gt;&lt;span&gt;noExcessiveCognitiveComplexity ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  ⚠ Excessive complexity&lt;/span&gt;&lt;span class=&quot;z-13&quot;&gt; of&lt;/span&gt;&lt;span class=&quot;z-6&quot;&gt; 21&lt;/span&gt;&lt;span class=&quot;z-5&quot;&gt; detected&lt;/span&gt;&lt;span&gt; (max: &lt;/span&gt;&lt;span class=&quot;z-6&quot;&gt;15&lt;/span&gt;&lt;span&gt;).&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-13&quot;&gt;  &amp;gt;&lt;/span&gt;&lt;span class=&quot;z-6&quot;&gt; 1&lt;/span&gt;&lt;span&gt; │ &lt;/span&gt;&lt;span class=&quot;z-1 z-i&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;z-5&quot;&gt; tooComplex&lt;/span&gt;&lt;span&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      │          &lt;/span&gt;&lt;span class=&quot;z-13&quot;&gt;^^^^^^^^^^&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-6&quot;&gt;    2&lt;/span&gt;&lt;span&gt; │     &lt;/span&gt;&lt;span class=&quot;z-13&quot;&gt;for&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span class=&quot;z-1 z-i&quot;&gt;let&lt;/span&gt;&lt;span&gt; x&lt;/span&gt;&lt;span class=&quot;z-13&quot;&gt; =&lt;/span&gt;&lt;span class=&quot;z-6&quot;&gt; 0&lt;/span&gt;&lt;span&gt;; x&lt;/span&gt;&lt;span class=&quot;z-13&quot;&gt; &amp;lt;&lt;/span&gt;&lt;span class=&quot;z-6&quot;&gt; 10&lt;/span&gt;&lt;span&gt;; x&lt;/span&gt;&lt;span class=&quot;z-13&quot;&gt;++&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-6&quot;&gt;    3&lt;/span&gt;&lt;span&gt; │         &lt;/span&gt;&lt;span class=&quot;z-13&quot;&gt;for&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span class=&quot;z-1 z-i&quot;&gt;let&lt;/span&gt;&lt;span&gt; y&lt;/span&gt;&lt;span class=&quot;z-13&quot;&gt; =&lt;/span&gt;&lt;span class=&quot;z-6&quot;&gt; 0&lt;/span&gt;&lt;span&gt;; y&lt;/span&gt;&lt;span class=&quot;z-13&quot;&gt; &amp;lt;&lt;/span&gt;&lt;span class=&quot;z-6&quot;&gt; 10&lt;/span&gt;&lt;span&gt;; y&lt;/span&gt;&lt;span class=&quot;z-13&quot;&gt;++&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4&quot;&gt;// 3. for .. of  vs  Array.forEach&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;code&lt;/span&gt;&lt;span class=&quot;z-13&quot;&gt;-&lt;/span&gt;&lt;span&gt;block.js:&lt;/span&gt;&lt;span class=&quot;z-6&quot;&gt;1&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span class=&quot;z-6&quot;&gt;1&lt;/span&gt;&lt;span&gt; lint&lt;/span&gt;&lt;span class=&quot;z-13&quot;&gt;/&lt;/span&gt;&lt;span&gt;complexity&lt;/span&gt;&lt;span class=&quot;z-13&quot;&gt;/&lt;/span&gt;&lt;span&gt;noForEach ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  ✖ Prefer for…&lt;/span&gt;&lt;span class=&quot;z-13&quot;&gt;of&lt;/span&gt;&lt;span&gt; instead&lt;/span&gt;&lt;span class=&quot;z-13&quot;&gt; of&lt;/span&gt;&lt;span&gt; forEach.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-13&quot;&gt;  &amp;gt;&lt;/span&gt;&lt;span class=&quot;z-6&quot;&gt; 1&lt;/span&gt;&lt;span&gt; │ els.&lt;/span&gt;&lt;span class=&quot;z-5&quot;&gt;forEach&lt;/span&gt;&lt;span&gt;((&lt;/span&gt;&lt;span class=&quot;z-14 z-i&quot;&gt;el&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span class=&quot;z-1 z-i&quot;&gt; =&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      │ &lt;/span&gt;&lt;span class=&quot;z-13&quot;&gt;^^^^^^^^^^^^^^^^^^^^^&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-13&quot;&gt;  &amp;gt;&lt;/span&gt;&lt;span class=&quot;z-6&quot;&gt; 2&lt;/span&gt;&lt;span&gt; │   &lt;/span&gt;&lt;span class=&quot;z-5&quot;&gt;f&lt;/span&gt;&lt;span&gt;(el);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-13&quot;&gt;  &amp;gt;&lt;/span&gt;&lt;span class=&quot;z-6&quot;&gt; 3&lt;/span&gt;&lt;span&gt; │ })&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      │ &lt;/span&gt;&lt;span class=&quot;z-13&quot;&gt;^^&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4&quot;&gt;// 4. no useless catch&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;code&lt;/span&gt;&lt;span class=&quot;z-13&quot;&gt;-&lt;/span&gt;&lt;span&gt;block.js:&lt;/span&gt;&lt;span class=&quot;z-6&quot;&gt;4&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span class=&quot;z-6&quot;&gt;5&lt;/span&gt;&lt;span&gt; lint&lt;/span&gt;&lt;span class=&quot;z-13&quot;&gt;/&lt;/span&gt;&lt;span&gt;complexity&lt;/span&gt;&lt;span class=&quot;z-13&quot;&gt;/&lt;/span&gt;&lt;span&gt;noUselessCatch ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  ✖ The&lt;/span&gt;&lt;span class=&quot;z-13&quot;&gt; catch&lt;/span&gt;&lt;span&gt; clause that only rethrows the original error is useless.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-6&quot;&gt;    2&lt;/span&gt;&lt;span&gt; │     &lt;/span&gt;&lt;span class=&quot;z-5&quot;&gt;doSomething&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-6&quot;&gt;    3&lt;/span&gt;&lt;span&gt; │ }&lt;/span&gt;&lt;span class=&quot;z-13&quot;&gt; catch&lt;/span&gt;&lt;span&gt;(e) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-13&quot;&gt;  &amp;gt;&lt;/span&gt;&lt;span class=&quot;z-6&quot;&gt; 4&lt;/span&gt;&lt;span&gt; │     &lt;/span&gt;&lt;span class=&quot;z-13&quot;&gt;throw&lt;/span&gt;&lt;span&gt; e;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      │     &lt;/span&gt;&lt;span class=&quot;z-13&quot;&gt;^^^^^^^^&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4&quot;&gt;// 5. no useless ternary&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-1 z-i&quot;&gt;const&lt;/span&gt;&lt;span&gt; a&lt;/span&gt;&lt;span class=&quot;z-13&quot;&gt; =&lt;/span&gt;&lt;span&gt; foo&lt;/span&gt;&lt;span class=&quot;z-13&quot;&gt; ===&lt;/span&gt;&lt;span class=&quot;z-6&quot;&gt; 1&lt;/span&gt;&lt;span class=&quot;z-13&quot;&gt; ?&lt;/span&gt;&lt;span class=&quot;z-6&quot;&gt; false&lt;/span&gt;&lt;span class=&quot;z-13&quot;&gt; :&lt;/span&gt;&lt;span class=&quot;z-6&quot;&gt; true&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt; // einfach foo !== 1 schreiben&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;code&lt;/span&gt;&lt;span class=&quot;z-13&quot;&gt;-&lt;/span&gt;&lt;span&gt;block.js:&lt;/span&gt;&lt;span class=&quot;z-6&quot;&gt;1&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span class=&quot;z-6&quot;&gt;9&lt;/span&gt;&lt;span&gt; lint&lt;/span&gt;&lt;span class=&quot;z-13&quot;&gt;/&lt;/span&gt;&lt;span&gt;complexity&lt;/span&gt;&lt;span class=&quot;z-13&quot;&gt;/&lt;/span&gt;&lt;span&gt;noUselessTernary  FIXABLE  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  ✖ Unnecessary use&lt;/span&gt;&lt;span class=&quot;z-13&quot;&gt; of&lt;/span&gt;&lt;span&gt; boolean literals&lt;/span&gt;&lt;span class=&quot;z-13&quot;&gt; in&lt;/span&gt;&lt;span&gt; conditional expression.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-13&quot;&gt;  &amp;gt;&lt;/span&gt;&lt;span class=&quot;z-6&quot;&gt; 1&lt;/span&gt;&lt;span&gt; │ &lt;/span&gt;&lt;span class=&quot;z-1 z-i&quot;&gt;const&lt;/span&gt;&lt;span&gt; a&lt;/span&gt;&lt;span class=&quot;z-13&quot;&gt; =&lt;/span&gt;&lt;span&gt; foo&lt;/span&gt;&lt;span class=&quot;z-13&quot;&gt; ===&lt;/span&gt;&lt;span class=&quot;z-6&quot;&gt; 1&lt;/span&gt;&lt;span class=&quot;z-13&quot;&gt; ?&lt;/span&gt;&lt;span class=&quot;z-6&quot;&gt; false&lt;/span&gt;&lt;span class=&quot;z-13&quot;&gt; :&lt;/span&gt;&lt;span class=&quot;z-6&quot;&gt; true&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      │           &lt;/span&gt;&lt;span class=&quot;z-13&quot;&gt;^^^^^^^^^^^^^^^^^^^^^^^^&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;ol&gt;
&lt;li&gt;Trivial, aber wichtig: keine unnötigen/schädlichen Typen&lt;/li&gt;
&lt;li&gt;Nutzt Cyclomatic Complexity als Obergrenze für komplexen Code. Zu komplex → vereinfachen. Mehr Details unter &lt;a rel=&quot;external&quot; href=&quot;https://en.wikipedia.org/wiki/Cyclomatic_complexity&quot;&gt;McCabe Metrik&lt;/a&gt;. Wir sollten unter 10 bleiben (10 mögliche Datenpfade durch eine Funktion) und MÜSSEN unter 15 bleiben.&lt;/li&gt;
&lt;li&gt;Sprachfeatures: &lt;code&gt;for .. of ..&lt;/code&gt; ist leichter zu lesen als &lt;code&gt;Array.forEach(...)&lt;/code&gt;. Auch ein Performance-Vorteil!&lt;/li&gt;
&lt;li&gt;Quasi toter Code ohne Funktion&lt;/li&gt;
&lt;li&gt;Ein schönes Beispiel für &lt;code&gt;FIXABLE&lt;/code&gt;-Lints: Dieser wird automatisch korrigiert!&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;... Und noch vieles mehr! Linter sollten in jeder Phase des Entwicklungsprozesses eingesetzt werden. Je früher, desto besser.&lt;/p&gt;
&lt;h2 id=&quot;biome-im-editor&quot;&gt;Biome im Editor&lt;/h2&gt;
&lt;p&gt;Für alle genannten Gründe sollten wir Biome überall einsetzen, wo es möglich ist.&lt;/p&gt;
&lt;p&gt;Für die gängigsten Editoren hier die Extension/Plugin-Links, damit die roten Wellenlinien direkt verschwinden und Auto-Formatierung aktiv ist:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Allgemein und Vim/Emacs/etc.:&lt;/strong&gt; &lt;a rel=&quot;external&quot; href=&quot;https://biomejs.dev/guides/integrate-in-editor/#use-the-daemon-with-the-binary&quot;&gt;biomejs.dev&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;WebStorm:&lt;/strong&gt; &lt;a rel=&quot;external&quot; href=&quot;https://plugins.jetbrains.com/plugin/22761-biome&quot;&gt;plugins.jetbrains.com&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;VSCode:&lt;/strong&gt; &lt;a rel=&quot;external&quot; href=&quot;https://marketplace.visualstudio.com/items?itemName=biomejs.biome&quot;&gt;VSCode Marketplace&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;CLI:&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;npm install --save-dev --save-exact @biomejs/biome&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Formatieren: &lt;code&gt;biome format ORDNER/DATEI&lt;/code&gt; oder &lt;code&gt;biome format --write ORDNER/DATEI&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Linten: &lt;code&gt;biome lint ORDNER/DATEI&lt;/code&gt; oder &lt;code&gt;biome lint --write ORDNER/DATEI&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Alle Checks (Format + Lint): &lt;code&gt;biome check ORDNER/DATEI&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Danach unbedingt:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Auto-Formatierung aktivieren&lt;/li&gt;
&lt;li&gt;Die Linter-Fehler tatsächlich lesen und beheben&lt;/li&gt;
&lt;/ol&gt;
</content>
        
    </entry>
    <entry xml:lang="de">
        <title>Magic Numbers</title>
        <published>2024-11-22T10:00:00+00:00</published>
        <updated>2024-11-24T10:52:23+00:00</updated>
        
        <author>
          <name>Unknown</name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://loki.dev/blog/magic-numbers/"/>
        <id>https://loki.dev/blog/magic-numbers/</id>
        
        <content type="html" xml:base="https://loki.dev/blog/magic-numbers/">&lt;p&gt;Magic Numbers sind Zahlen oder String-Literale, die als Konstanten eingesetzt werden, ohne dass ihre eigentliche Bedeutung direkt erkennbar ist.&lt;/p&gt;
&lt;p&gt;Dieser Artikel ist Teil einer Serie, die ich für die Arbeit schreibe, gedacht für Junior-Entwickler und externe Mitarbeiter.
Wenn der Code generisch genug ist, verwende ich echte Beispiele aus der Praxis, ansonsten erfinde ich etwas ;).&lt;/p&gt;
&lt;p&gt;Die Beispiele sind in TypeScript, da es von fast allen Entwicklern lesbar ist, die Konzepte gelten aber natürlich auch in anderen Sprachen. Python hat zum Beispiel keine echten &quot;Konstanten&quot;, aber man kann reines UPPERCASE mit &lt;code&gt;typing.Final&lt;/code&gt; kombinieren:&lt;/p&gt;
&lt;pre class=&quot;giallo z-code&quot; &gt;&lt;code data-lang=&quot;python&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-13&quot;&gt;from&lt;/span&gt;&lt;span&gt; typing&lt;/span&gt;&lt;span class=&quot;z-13&quot;&gt; import&lt;/span&gt;&lt;span&gt; Final&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-6&quot;&gt;MAX_KNOWN_POWER_SIZE&lt;/span&gt;&lt;span&gt;: Final[&lt;/span&gt;&lt;span class=&quot;z-1 z-i&quot;&gt;int&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span class=&quot;z-13&quot;&gt; =&lt;/span&gt;&lt;span class=&quot;z-6&quot;&gt; 9000&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Das verhindert keine Neuzuweisung zur Laufzeit, aber &lt;a rel=&quot;external&quot; href=&quot;https://astral.sh/ruff&quot;&gt;ruff&lt;/a&gt; oder &lt;a rel=&quot;external&quot; href=&quot;https://www.mypy-lang.org&quot;&gt;mypy&lt;/a&gt; werden sich beschweren.&lt;/p&gt;
&lt;h2 id=&quot;beispiele&quot;&gt;Beispiele&lt;/h2&gt;
&lt;h3 id=&quot;http-status-codes&quot;&gt;HTTP-Status-Codes&lt;/h3&gt;
&lt;pre class=&quot;giallo z-code&quot; &gt;&lt;code data-lang=&quot;typescript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4&quot;&gt;// FALSCH&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-13&quot;&gt;if&lt;/span&gt;&lt;span&gt; (error.response.status&lt;/span&gt;&lt;span class=&quot;z-13&quot;&gt; ===&lt;/span&gt;&lt;span class=&quot;z-6&quot;&gt; 409&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-13&quot;&gt;    ...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4&quot;&gt;// RICHTIG&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-13&quot;&gt;import&lt;/span&gt;&lt;span&gt; { HttpStatus }&lt;/span&gt;&lt;span class=&quot;z-13&quot;&gt; from&lt;/span&gt;&lt;span class=&quot;z-11&quot;&gt; &amp;#39;@nestjs/common&amp;#39;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-13&quot;&gt;if&lt;/span&gt;&lt;span&gt; (error.response.status&lt;/span&gt;&lt;span class=&quot;z-13&quot;&gt; ===&lt;/span&gt;&lt;span&gt; HttpStatus.CONFLICT) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-13&quot;&gt;    ...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Selbst wenn wir nicht alle HTTP-Status-Codes auswendig kennen, wissen wir jetzt, dass hier ein Konflikt vorliegt, ganz ohne Kommentare oder zusätzliche Komplexität.&lt;/p&gt;
&lt;h3 id=&quot;timeout&quot;&gt;Timeout&lt;/h3&gt;
&lt;pre class=&quot;giallo z-code&quot; &gt;&lt;code data-lang=&quot;typescript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4&quot;&gt;// FALSCH&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-5&quot;&gt;setTimeout&lt;/span&gt;&lt;span&gt;(()&lt;/span&gt;&lt;span class=&quot;z-1 z-i&quot;&gt; =&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-5&quot;&gt;    refreshData&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;},&lt;/span&gt;&lt;span class=&quot;z-6&quot;&gt; 300000&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt; // Was bedeutet diese Zahl?&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4&quot;&gt;// RICHTIG&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-1 z-i&quot;&gt;const&lt;/span&gt;&lt;span&gt; REFRESH_INTERVAL_MS&lt;/span&gt;&lt;span class=&quot;z-13&quot;&gt; =&lt;/span&gt;&lt;span class=&quot;z-6&quot;&gt; 300000&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt; // 5 Minuten in Millisekunden&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-5&quot;&gt;setTimeout&lt;/span&gt;&lt;span&gt;(()&lt;/span&gt;&lt;span class=&quot;z-1 z-i&quot;&gt; =&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-5&quot;&gt;    refreshData&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}, REFRESH_INTERVAL_MS);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&quot;limits-geschaftslogik&quot;&gt;Limits: Geschäftslogik&lt;/h3&gt;
&lt;pre class=&quot;giallo z-code&quot; &gt;&lt;code data-lang=&quot;typescript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4&quot;&gt;// FALSCH&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-13&quot;&gt;if&lt;/span&gt;&lt;span&gt; (user.age&lt;/span&gt;&lt;span class=&quot;z-13&quot;&gt; &amp;gt;=&lt;/span&gt;&lt;span class=&quot;z-6&quot;&gt; 18&lt;/span&gt;&lt;span class=&quot;z-13&quot;&gt; &amp;amp;&amp;amp;&lt;/span&gt;&lt;span&gt; cart.total&lt;/span&gt;&lt;span class=&quot;z-13&quot;&gt; &amp;gt;=&lt;/span&gt;&lt;span class=&quot;z-6&quot;&gt; 50&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-5&quot;&gt;    applyDiscount&lt;/span&gt;&lt;span&gt;(cart);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4&quot;&gt;// RICHTIG&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-1 z-i&quot;&gt;const&lt;/span&gt;&lt;span&gt; MINIMUM_AGE_FOR_DISCOUNT&lt;/span&gt;&lt;span class=&quot;z-13&quot;&gt; =&lt;/span&gt;&lt;span class=&quot;z-6&quot;&gt; 18&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-1 z-i&quot;&gt;const&lt;/span&gt;&lt;span&gt; MINIMUM_PURCHASE_FOR_DISCOUNT&lt;/span&gt;&lt;span class=&quot;z-13&quot;&gt; =&lt;/span&gt;&lt;span class=&quot;z-6&quot;&gt; 50&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-13&quot;&gt;if&lt;/span&gt;&lt;span&gt; (user.age&lt;/span&gt;&lt;span class=&quot;z-13&quot;&gt; &amp;gt;=&lt;/span&gt;&lt;span&gt; MINIMUM_AGE_FOR_DISCOUNT&lt;/span&gt;&lt;span class=&quot;z-13&quot;&gt; &amp;amp;&amp;amp;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    cart.total&lt;/span&gt;&lt;span class=&quot;z-13&quot;&gt; &amp;gt;=&lt;/span&gt;&lt;span&gt; MINIMUM_PURCHASE_FOR_DISCOUNT) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-5&quot;&gt;    applyDiscount&lt;/span&gt;&lt;span&gt;(cart);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&quot;limits-passwort&quot;&gt;Limits: Passwort&lt;/h3&gt;
&lt;pre class=&quot;giallo z-code&quot; &gt;&lt;code data-lang=&quot;typescript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4&quot;&gt;// FALSCH&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-13&quot;&gt;if&lt;/span&gt;&lt;span&gt; (password.length&lt;/span&gt;&lt;span class=&quot;z-13&quot;&gt; &amp;lt;&lt;/span&gt;&lt;span class=&quot;z-6&quot;&gt; 8&lt;/span&gt;&lt;span class=&quot;z-13&quot;&gt; ||&lt;/span&gt;&lt;span&gt; password.length&lt;/span&gt;&lt;span class=&quot;z-13&quot;&gt; &amp;gt;&lt;/span&gt;&lt;span class=&quot;z-6&quot;&gt; 128&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-13&quot;&gt;    throw new&lt;/span&gt;&lt;span class=&quot;z-5&quot;&gt; Error&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span class=&quot;z-11&quot;&gt;&amp;#39;Invalid password length&amp;#39;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4&quot;&gt;// RICHTIG&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-1 z-i&quot;&gt;const&lt;/span&gt;&lt;span&gt; PASSWORD_LENGTH_MIN&lt;/span&gt;&lt;span class=&quot;z-13&quot;&gt; =&lt;/span&gt;&lt;span class=&quot;z-6&quot;&gt; 8&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-1 z-i&quot;&gt;const&lt;/span&gt;&lt;span&gt; PASSWORD_LENGTH_MAX&lt;/span&gt;&lt;span class=&quot;z-13&quot;&gt; =&lt;/span&gt;&lt;span class=&quot;z-6&quot;&gt; 128&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-13&quot;&gt;if&lt;/span&gt;&lt;span&gt; (password.length&lt;/span&gt;&lt;span class=&quot;z-13&quot;&gt; &amp;lt;&lt;/span&gt;&lt;span&gt; PASSWORD_LENGTH_MIN&lt;/span&gt;&lt;span class=&quot;z-13&quot;&gt; ||&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    password.length&lt;/span&gt;&lt;span class=&quot;z-13&quot;&gt; &amp;gt;&lt;/span&gt;&lt;span&gt; PASSWORD_LENGTH_MAX) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-13&quot;&gt;    throw new&lt;/span&gt;&lt;span class=&quot;z-5&quot;&gt; Error&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span class=&quot;z-11&quot;&gt;&amp;#39;Invalid password length&amp;#39;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;</content>
        
    </entry>
    <entry xml:lang="de">
        <title>Python Unplugged: Verborgene Batterien schürfen</title>
        <published>2024-07-10T00:00:00+00:00</published>
        <updated>2024-07-15T13:42:00+00:00</updated>
        
        <author>
          <name>Unknown</name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://loki.dev/vortraege/europython2024-python-batteries-included/"/>
        <id>https://loki.dev/vortraege/europython2024-python-batteries-included/</id>
        
        <content type="html" xml:base="https://loki.dev/vortraege/europython2024-python-batteries-included/">&lt;div class=&quot;youtube&quot;&gt;
  &lt;iframe src=&quot;https://www.youtube.com/embed/W5ZOFvGmmbo&quot; webkitallowfullscreen mozallowfullscreen allowfullscreen&gt;&lt;/iframe&gt;
&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Slides (PDF):&lt;/strong&gt; &lt;a rel=&quot;external&quot; href=&quot;https://loki.dev/slides/vortraege/ep2024-python-unplugged/europython2024-python-unplugged-slides.pdf&quot;&gt;PDF herunterladen&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Repository:&lt;/strong&gt; &lt;a rel=&quot;external&quot; href=&quot;https://github.com/dev-loki/europython2024-python-unplugged-slides&quot;&gt;github.com/dev-loki/europython2024-python-unplugged-slides&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;worum-geht-es&quot;&gt;Worum geht es?&lt;/h2&gt;
&lt;p&gt;Ein Einsteiger-Vortrag über all die unbekannten Module der Python-Standardbibliothek.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Python-Grundkenntnisse erforderlich&lt;/li&gt;
&lt;li&gt;Kein &lt;code&gt;pip install PACKAGE&lt;/code&gt; nötig &amp;lt;3&lt;/li&gt;
&lt;li&gt;Schwerpunkte: &lt;code&gt;itertools&lt;/code&gt;, &lt;code&gt;collections&lt;/code&gt; und &lt;code&gt;functools&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Ausflüge in &lt;code&gt;contextlib&lt;/code&gt;, &lt;code&gt;urllib&lt;/code&gt; und weitere Pakete&lt;/li&gt;
&lt;li&gt;Einiges erfordert &lt;code&gt;python3.12&lt;/code&gt; (z.B. &lt;code&gt;batched&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Das Repository enthält weitere Code-Beispiele, die:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;detaillierter sind&lt;/li&gt;
&lt;li&gt;ausführlich kommentiert sind&lt;/li&gt;
&lt;li&gt;„vollständig&quot; und ausführbar sind&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;em&gt;(Vortrag auf Englisch)&lt;/em&gt;&lt;/p&gt;
</content>
        
    </entry>
    <entry xml:lang="de">
        <title>Schneller entwickeln mit Test-Driven Development</title>
        <published>2022-04-12T00:00:00+00:00</published>
        <updated>2023-01-29T12:07:00+00:00</updated>
        
        <author>
          <name>Unknown</name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://loki.dev/vortraege/tdd/"/>
        <id>https://loki.dev/vortraege/tdd/</id>
        
        <content type="html" xml:base="https://loki.dev/vortraege/tdd/">&lt;p&gt;&lt;em&gt;(Vortrag auf Englisch)&lt;/em&gt;&lt;/p&gt;
&lt;h2 id=&quot;original-abstract&quot;&gt;Original-Abstract&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;In diesem Vortrag zeige ich, dass TDD eigentlich kein Prozess für das Testen ist, sondern eine hilfreiche Entwicklungsmethode, die das lästige „Lass mal die Anwendung aufmachen und schauen ob das noch funktioniert&quot; weitgehend eliminiert.&lt;/p&gt;
&lt;p&gt;Nach dem Vortrag wisst ihr, wie ihr TDD in euren Workflow integriert und zu eurem Vorteil nutzen könnt. Wir räumen mit dem verbreiteten Missverständnis auf, dass TDD eine Testtechnik ist, und haben am Ende einen funktionierenden Prototypen.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;nachbetrachtung&quot;&gt;Nachbetrachtung&lt;/h2&gt;
&lt;p&gt;In diesem Vortrag habe ich gezeigt, wie man einen minimalen URL-Shortener mit FastAPI/pytest im TDD-Stil baut. Es war fast erfolgreich, ich hatte nicht mit so vielen Fragen gerechnet ;).&lt;/p&gt;
&lt;p&gt;Generell war ich &lt;strong&gt;viel&lt;/strong&gt; zu nervös für eine gute Performance, und auch wenn mir Leute Komplimente gemacht haben, glaube ich, dass ich es deutlich besser hätte machen können!&lt;/p&gt;
&lt;p&gt;Meine Erkenntnis: 1h 30min war zu wenig Zeit um wirklich tief in die TDD-Methodik einzutauchen. 2h wären ideal gewesen.&lt;/p&gt;
&lt;p&gt;Mein Ziel: Das nach dem Jobwechsel (mit vielen neuen Dingen zum Lernen) verbessern.&lt;/p&gt;
&lt;h2 id=&quot;wird-es-einen-richtigen-kurs-geben&quot;&gt;Wird es einen richtigen Kurs geben?&lt;/h2&gt;
&lt;p&gt;Wahrscheinlich ja, für den nicht-konferenz-Einsatz würde ich aber lieber einen intensiveren 2-4h-Kurs machen, der auch Themen wie Datenbank-Migration und Deployment abdeckt.&lt;/p&gt;
&lt;h2 id=&quot;kann-man-es-sehen&quot;&gt;Kann man es sehen?&lt;/h2&gt;
&lt;p&gt;Ja, wenn es sein muss :D. Aber Achtung: Mein Englisch war sehr eingerostet, da ich 1-2 Jahre lang kein Englisch gesprochen hatte. Ich bin super froh, dass es mit dem neuen Job wieder besser geworden ist :)&lt;/p&gt;
&lt;div class=&quot;youtube&quot;&gt;
  &lt;iframe src=&quot;https://www.youtube.com/embed/_6AaG-cgMjk&quot; webkitallowfullscreen mozallowfullscreen allowfullscreen&gt;&lt;/iframe&gt;
&lt;/div&gt;
</content>
        
    </entry>
    <entry xml:lang="de">
        <title>Advanced GIT: Git Worktree</title>
        <published>2021-11-25T00:00:00+00:00</published>
        <updated>2021-11-25T00:00:00+00:00</updated>
        
        <author>
          <name>Unknown</name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://loki.dev/blog/advanced-git-worktree/"/>
        <id>https://loki.dev/blog/advanced-git-worktree/</id>
        
        <content type="html" xml:base="https://loki.dev/blog/advanced-git-worktree/">&lt;p&gt;Eines der geilsten Features von Git. Gleichzeitig eines der am wenigsten benutzten Features.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Folgende Situation:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Ihr seid gerade an eurem &lt;code&gt;feature/blubb_feature&lt;/code&gt; dran und dann kommt ein MR vom Kollegen,
wo ihr bitte ganz schnell &lt;code&gt;hotfix/make_that_users_dont_die_immediately&lt;/code&gt; reviewen sollt,
damit das super flott live gehen kann. Leider ist der branch zu komplex um alles direkt im
Gitlab zu machen.&lt;/p&gt;
&lt;p&gt;Also wirklich auschecken und deep-dive in den Code...&lt;/p&gt;
&lt;h3 id=&quot;klassisches-vorgehen&quot;&gt;Klassisches Vorgehen&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;git stash&lt;/code&gt; eurer Änderungen&lt;/li&gt;
&lt;li&gt;&lt;code&gt;git checkout hotfix/...&lt;/code&gt; um den Branch anzugucken und alles durch zu gehen.&lt;/li&gt;
&lt;li&gt;Freigabe oder eben nicht&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;viel-besseres-vorgehen&quot;&gt;Viel besseres Vorgehen&lt;/h3&gt;
&lt;p&gt;Ihr habt &lt;code&gt;git worktree&lt;/code&gt; eingerichtet (ist nativ bei git dabei) und habt sowieso schon folgende Ordnerstruktur:&lt;/p&gt;
&lt;pre class=&quot;giallo z-code&quot; &gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;project_name/&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt; - main/&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt; - develop/&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt; - feature/&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;   - blubb_feature/&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt; - hotfix/&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;   - make_that_users_dont_die_immediately/&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Jeder Ordner ist quasi ein eigenes Git Repository. Aber alle teilen sich die
Git Metadaten. Statt also euren Stand zu verlassen und komplett zu wechseln,
reicht ein &lt;code&gt;git worktree add BRANCH_NAME&lt;/code&gt; und ein Ordner, der auf einen zeigt,
wird für euch erstellt.&lt;/p&gt;
&lt;h2 id=&quot;aber-wie-geht-das-jetzt&quot;&gt;Aber wie geht das jetzt?&lt;/h2&gt;
&lt;p&gt;Super simpel - wirklich :).&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;mkdir my_project&lt;/code&gt; (Projektverzeichnis erstellen) und via &lt;code&gt;cd&lt;/code&gt; dort rein wechseln: &lt;code&gt;cd my_project&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Repository klonen (Achtung - hier ist die erste Besonderheit) &lt;code&gt;git clone --bare GIT_URL .bare&lt;/code&gt;.
&lt;ul&gt;
&lt;li&gt;Wir klonen also nur das bare Repository ohne einen Branch auszuchecken&lt;/li&gt;
&lt;li&gt;Diese Metadaten speichern wir im Order &lt;code&gt;.bare&lt;/code&gt; (Durch den &lt;code&gt;.&lt;/code&gt; am Anfang ist der Ordner unter MacOS/Linux versteckt)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Eine Datei &lt;code&gt;my_project/.git&lt;/code&gt; erstellen und folgende Zeile eintragen:&lt;pre class=&quot;giallo z-code&quot; &gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;gitdir: ./.bare&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;Damit weiß Git jetzt, wo sich unsere Repository Metadaten befinden&lt;/li&gt;
&lt;li&gt;Ab jetzt funktioniert worktree perfekt:
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;git worktree add main&lt;/code&gt; fügt unseren Main-Branch hinzu&lt;/li&gt;
&lt;li&gt;&lt;code&gt;git worktree add feature/ABC_new_stuff_to_play feature/ABC_new_stuff_to_play&lt;/code&gt; um Branches mit &lt;code&gt;/&lt;/code&gt; im Namen auszuchecken oder zu erstellen&lt;/li&gt;
&lt;li&gt;Tatsächlich könnt ihr aber auch beliebig im Dateisystem euren Ordner mit dem Branch erstellen. Das Format sieht in etwa so aus:
&lt;code&gt;git worktree add PATH BRANCHNAME&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;weitere-hilfreiche-befehle&quot;&gt;Weitere hilfreiche Befehle&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Branch löschen:
&lt;ul&gt;
&lt;li&gt;Variante 1: &lt;code&gt;rm -r BRANCH_NAME&lt;/code&gt; und dann &lt;code&gt;git worktree prune&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Variante 2: &lt;code&gt;git worktree list&lt;/code&gt; Listet alle worktree branches.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Branch verschieben: &lt;code&gt;git worktree move SOURCE DESTINATION&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
</content>
        
    </entry>
    <entry xml:lang="de">
        <title>Stressfrei Programmieren mit Clean Code I</title>
        <published>2021-09-02T00:00:00+00:00</published>
        <updated>2022-05-30T14:30:00+00:00</updated>
        
        <author>
          <name>Unknown</name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://loki.dev/vortraege/clean-code-i/"/>
        <id>https://loki.dev/vortraege/clean-code-i/</id>
        
        <content type="html" xml:base="https://loki.dev/vortraege/clean-code-i/">&lt;p&gt;&lt;strong&gt;Talk aktualisiert:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Auf englisch übersetzt&lt;/li&gt;
&lt;li&gt;Fehler bereinigt&lt;/li&gt;
&lt;li&gt;Dart spezifische Teile entfernt&lt;/li&gt;
&lt;li&gt;An &lt;a rel=&quot;external&quot; href=&quot;https://mara.solutions&quot;&gt;neuen Arbeitgeber&lt;/a&gt; angepasst :)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;folien&quot;&gt;Folien&lt;/h2&gt;
&lt;iframe src=&quot;https://loki.dev/slides/vortraege/clean-code-i/&quot; style=&quot;width:100%;height:400px&quot; title=&quot;Clean Code I Slides&quot;&gt;
&lt;/iframe&gt;
</content>
        
    </entry>
    <entry xml:lang="de">
        <title>Wahlhelfer zur Bundestagswahl 2021</title>
        <published>2021-08-23T00:00:00+00:00</published>
        <updated>2021-09-05T14:00:00+00:00</updated>
        
        <author>
          <name>Unknown</name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://loki.dev/blog/wahlhelfer-btw-2021/"/>
        <id>https://loki.dev/blog/wahlhelfer-btw-2021/</id>
        
        <content type="html" xml:base="https://loki.dev/blog/wahlhelfer-btw-2021/">&lt;p&gt;Da ich die Kritik am Wahl-O-Mat selber kenne und teile, stelle ich hier die
Alternative und Ergänzungen einmal vor.&lt;/p&gt;
&lt;p&gt;Um vollständig zu sein/bleiben, hier meine persönlichen Kritikpunkte am
Wahl-O-Maten:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Eindimensional -&amp;gt; Es fehlt die Abstufung oder auch eine &quot;so nicht, aber so:&quot;
Option&lt;/li&gt;
&lt;li&gt;Er gibt nur die Antworten der Politik wieder, aber nicht deren Handlung. Da
wäre es echt fein, wenn diese beiden Dinge nicht so häufig auseinander
driften würden.&lt;/li&gt;
&lt;li&gt;Es wird nur ein beschränktes politisches Arsenal aus Thesen besprochen.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;All diese Punkte haben auch irgendwie eine Berechtigung, da man natürlich nicht
beliebig in die Tiefe gehen kann.&lt;/p&gt;
&lt;p&gt;Trotzdem finde ich das Konzept des Wahl-O-Maten vom Grunde her super sinnvoll,
da es Diskussion ermöglicht und ein grobes &quot;Alignment&quot; ermitteln kann. Wenn ich
z.Bsp.  eine 2% Übereinstimmung mit der AfD/Linken/Whatever habe, dann ist die
Chance zwar nicht 0, aber doch sehr sehr gering, dass ich eben genau diese
Partei wählen sollte.&lt;/p&gt;
&lt;p&gt;Jetzt gibt es aber nicht nur den Wahl-O-Mat, sondern auch diverse alternative
Wahlhelfer mit unterschiedlichen Ausrichtungen, die ich hier sammeln und
vorstellen möchte&lt;/p&gt;
&lt;h2 id=&quot;deinwal-de&quot;&gt;DeinWal.de&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Link:&lt;/strong&gt; &lt;a rel=&quot;external&quot; href=&quot;http://deinwal.de&quot;&gt;deinwal.de&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;del&gt;Noch ist der aktuelle &quot;Wal&quot;helfer nicht online&lt;/del&gt;, aber das Konzept von deinwal.de
ist mein absoluter Favorit! Er vergleicht nämlich keine Wahlprogramme oder das, was
Parteien gerade behaupten zu tun, sondern vergangene Abstimmungen und wie sehr
das mit der eigenen Meinung übereinstimmt.&lt;/p&gt;
&lt;p&gt;Auch wenn die BTW21 noch nicht frei geschaltet ist, kann man die alten BTW
absolvieren und gucken inwiefern die großen (denn nur die sind enthalten)
Parteien &quot;damals&quot; abgestimmt haben. Tipp: Europawahl 2019 und BTW2017
durchspielen.&lt;/p&gt;
&lt;p&gt;Zu jeder Frage gibt es detaillierte Informationen und Verweise direkt auf die
Pressemitteilung des Parlaments und Links zu abgeordnetenwatch sowie Wikipedia.&lt;/p&gt;
&lt;p&gt;Jede Abstimmung wird mit Ja/Nein/Enthaltung bewertet und am Ende eingeordnet.
Eine Gewichtung gibt es nicht.&lt;/p&gt;
&lt;p&gt;Auf jeden Fall trotz der fehlenden Kleinparteien eine klare Empfehlung, weil es
gerade den politischen Interessierten noch einmal enorm viele Zusatzinfos
liefert.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Edit:&lt;/strong&gt; Endlich ist die neueste Version von &lt;strong&gt;deinWal&lt;/strong&gt; online.  Das
Grundkonzept ist geblieben. Auch der hauptnachteil ist leider geblieben: Es sind
nur etablierte Parteien im Boot.&lt;/p&gt;
&lt;h2 id=&quot;wahlswiper&quot;&gt;Wahlswiper&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Link:&lt;/strong&gt; &lt;a rel=&quot;external&quot; href=&quot;https://www.voteswiper.org/de&quot;&gt;voteswiper.org&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Hier kann man, ähnlich wie es von Tinder bekannt ist, politische Statements
gut/schlecht finden und am Ende die Übereinstimmung mit den Parteien finden.&lt;/p&gt;
&lt;p&gt;Die gute Nachricht: Es treten 35 von 39 Parteien an, sodass man z.Bsp auch
kleine Parteien wie die &lt;a rel=&quot;external&quot; href=&quot;https://liebe-ep.eu&quot;&gt;LIEBE&lt;/a&gt; Partei oder die &lt;a rel=&quot;external&quot; href=&quot;https://www.lobbyistenfuerkinder.de&quot;&gt;Partei
für Kinder, Jugendliche und Familien&lt;/a&gt;
findet.&lt;/p&gt;
&lt;p&gt;Als Bonus gibt es zu jeder Frage ein kleines Video und einen kleinen Text,
welche sachlich das aktuelle Thema erklären.  Jede Frage kann mit
Zustimmung/Ablehnung oder Enthaltung bewertet werden.  Zusätzlich können
Themen, die einem besonders wichtig sind, doppelt gewichtet werden.&lt;/p&gt;
&lt;p&gt;Auch der Wahlswiper ist für mich eine gute Empfehlung für Anfänger, da gerade
Menschen, die sonst nicht so viel mit Politik am Hut haben, eine kleine
Übersicht bekommen.&lt;/p&gt;
&lt;h2 id=&quot;sozial-o-mat&quot;&gt;Sozial-O-Mat&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Link:&lt;/strong&gt; &lt;a rel=&quot;external&quot; href=&quot;https://www.sozial-o-mat.de&quot;&gt;sozial-o-mat.de&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Ganz speziell nur auf die Sozialpolitik fokussiert, gibt es den Sozial-O-Mat,
welcher nur aufgrund von Sozialpolitik die eigene Meinung mit der der Parteien
vergleicht. Dabei werden die vier folgenden Themengebiete abgedeckt:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Arbeit&lt;/li&gt;
&lt;li&gt;Gesundheit&lt;/li&gt;
&lt;li&gt;Famlie und Kinder&lt;/li&gt;
&lt;li&gt;Migration&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Betrieben wird der Sozial-O-Mat von der Diakonie. Jedes der vier Themen wird
mit einer kurzen Einleitung begonnen, die die Relevanz hervorheben sollen.&lt;/p&gt;
&lt;p&gt;Statt Videos oder Erklärungen wird dann jede These mit einer &quot;Geschichte&quot;
unterstützt. Also statt Informationen erhalten wir etwas Menschennäher die
Geschichte einer Person um diese These einzuordnen. Generell finde ich das
Konzept zwar interessant, aber es gibt natürlich auch etwas die Richtung vor!&lt;/p&gt;
&lt;p&gt;Jede These kann man als Positiv/Neutral/Negativ bewerten oder sie sogar
überspringen um sie aus der Gleichung zu nehmen.&lt;/p&gt;
&lt;p&gt;Gut für einen groben Überblick und für mich eher als Ergänzung gedacht.&lt;/p&gt;
&lt;h2 id=&quot;steuer-o-mat&quot;&gt;Steuer-O-Mat&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Link:&lt;/strong&gt; &lt;a rel=&quot;external&quot; href=&quot;https://www.steuer-o-mat.de/start&quot;&gt;smartsteuer&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Einer der kürzesten und deutlichsten Wahlhelfer ist der Steuer-O-Mat. Sehr
eindimensional wird ausschließlich das voraussichtliche Netto berechnet, was
man mit diversen Parteien hätte.&lt;/p&gt;
&lt;p&gt;Dazu gibt man einfach seinen Lohn ein und ggfs. noch den seines verheirateten
Partners und kriegt am Ende eine reine Steuer-Wahlempfehlung.&lt;/p&gt;
&lt;h2 id=&quot;agrar-o-mat&quot;&gt;Agrar-O-Mat&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Link:&lt;/strong&gt; &lt;a rel=&quot;external&quot; href=&quot;https://www.agrarheute.com/agraromat&quot;&gt;agrarheute.com&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Da - wie wir alle Wissen - die Lebensmittel natürlich nur aus dem Supermarkt
kommen, betrifft dieser Wahl-O-Mat ausschließlich die Bauern ;).&lt;/p&gt;
&lt;p&gt;Tatsächlich bezieht sich dieser Helfer passend zum Namen ausschließlich auf die
Agrarpolitik und damit angrenzende Felder.&lt;/p&gt;
&lt;p&gt;Zusätzliche Informationen gibt es nicht.&lt;/p&gt;
&lt;p&gt;Jeder These kann entweder zugestimmt werden, neutral bewertet werden oder
abgelehnt werden. Besonders wichtige Thesen können doppelt gewichtet werden.&lt;/p&gt;
&lt;p&gt;Für mich interessant als Spielerei und Ergänzung, aber natürlich fehlt das
Fachwissen, um wirklich Tief in die Agrarpolitik einzusteigen.&lt;/p&gt;
&lt;h2 id=&quot;klima-wahl-check&quot;&gt;Klima-Wahl-Check&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Link:&lt;/strong&gt; &lt;a rel=&quot;external&quot; href=&quot;https://klimawahlcheck.org&quot;&gt;klimawahlcheck.org&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Auf das Klima selber gehe ich &lt;a href=&quot;https://loki.dev/blog/klimakatastrophe-i-was-ist/&quot;&gt;hier im Blog ja auch schon
ein&lt;/a&gt; und werde definitiv noch deutlich
mehr eingehen, aber ganz speziell nur auf das Klima bezogen gibt es den
Klimawahlcheck.&lt;/p&gt;
&lt;p&gt;Ingesamt ist dieser in die 6 Kategorien unterteilt:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Energie&lt;/li&gt;
&lt;li&gt;Mobilität&lt;/li&gt;
&lt;li&gt;Industrie&lt;/li&gt;
&lt;li&gt;Gebäude&lt;/li&gt;
&lt;li&gt;Klimagerechtigkeit &amp;amp; Klimaziele&lt;/li&gt;
&lt;li&gt;Landwirtschaft &amp;amp; Artenvielfalt&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Für mich persönlich einer der wichtigsten Helfer überhaupt, da es hier nicht
nur, wie man gerne naiverweise denken möchte um das Klima geht, sondern
wirklich direkt alle sozialen, wirtschaftlichen und wissenschaftlichen Themen
mit abgedeckt werden.&lt;/p&gt;
&lt;p&gt;Im Gegensatz zu den anderen Helfern wird jedes Thema mit einer Zustimmung von
&lt;span style=&quot;background:red;color:yellow;padding:2px
4px;border-radius:3px&quot;&gt;rot&lt;/span&gt; bis &lt;span
style=&quot;background:green;color:white;padding:2px
4px;border-radius:3px&quot;&gt;grün&lt;/span&gt; in 5 Stufen unterteilt. Zusätzlich können
Thesen übersprungen werden.&lt;/p&gt;
&lt;p&gt;Zu jeder These gibt es zusätzlich eine kurze Zusammenfassung mit dem
Hintergrund zur Frage.&lt;/p&gt;
&lt;p&gt;Anschließend sieht man ein Übersichtsbild und sieht die Parteien
links/rechts/übereinstimmend von einem. Wenn man alle Kategorien durchgespielt
hat, gibt es eine Gesamtauswertung.&lt;/p&gt;
&lt;p&gt;Wie schon gesagt ist dieser Helfer für mich bisher der wichtigste, da die
Klimaentscheidungen signifikant in Wirtschaft, Lebensqualität und sogar
überleben mit einspielen.&lt;/p&gt;
&lt;p&gt;Klare Empfehlung. Natürlich ;).&lt;/p&gt;
&lt;h2 id=&quot;waehlbar2021&quot;&gt;Waehlbar2021&lt;/h2&gt;
&lt;p&gt;Link: &lt;a rel=&quot;external&quot; href=&quot;https://waehlbar2021.de&quot;&gt;waehlbar2021.de&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Deutlich mehr als nur ein &#39;o-mat bringt &lt;strong&gt;waehlbar2021&lt;/strong&gt; sowohl umfangreiche
Informationen mit, als auch detaillierte Positionen von fast allen Parteien.
Natürlich fehlt mal wieder die CDU/CSU in den Angaben. Die dachten sich wohl,
dass, wenn Sie gar nicht antworten, kann denen auch keiner etwas übel nehmen.&lt;/p&gt;
&lt;p&gt;Das beste Feature ist aber, direkt die Abgeordneten aus dem eigenen Wahlkreis
mit ihren Stimmen zu sehen. Dazu gibt mein die eigene PLZ an und bekommt alle
lokalen Kandidaten vorgeschlagen - inklusive der Übereinstimmungen in den
einzelnen Thesen.&lt;/p&gt;
&lt;p&gt;Abgeordneten die bisher nicht geantwortet haben, kann man hier direkt eine
Nachricht mit dem Wunsch nach Transparenz hinterlassen.&lt;/p&gt;
&lt;p&gt;Also auch hier natürlich eine klare Empfehlung zum Mitmachen und Informieren&lt;/p&gt;
&lt;h2 id=&quot;wahl-o-mat&quot;&gt;Wahl-o-Mat&lt;/h2&gt;
&lt;p&gt;Link: &lt;a rel=&quot;external&quot; href=&quot;https://www.wahl-o-mat.de/bundestagswahl2021/app/main_app.html&quot;&gt;wahl-o-mat.de&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Endlich ist er da. Als wohl bekanntester Wahlhelfer hat der Wahl-O-Mat insgesamt
39 der antretenden 40 Parteien &quot;interviewed&quot; um zu zahlreichen Fragen Antworten
zu finden.&lt;/p&gt;
&lt;p&gt;Tatsächlich muss ich zugeben, dass ich den Wahlhelfer dieses Jahr ein klein
bisschen besser als vorher finde. So gibt es nun zwar immer noch keine
detaillierten Informationen zu den Fragen bzw. nicht genug Abstufungen, aber
endlich haben wir die Möglichkeit sowohl direkt &lt;strong&gt;alle&lt;/strong&gt; Parteien zu
vergleichen (&lt;a rel=&quot;external&quot; href=&quot;https://www.volksverpetzer.de/schwer-verpetzt/volt-wahl-o-mat/&quot;&gt;vielen Dank
Volt&lt;/a&gt; dafür),
als auch die Möglichkeit, anschließend mit den diversen Ergebnissen und deren
Gewichtung herumzuspielen!&lt;/p&gt;
&lt;p&gt;Tatsächlich kam zumindest bei mir auch ein anderes Ergebnis raus, als bei den
anderen Wahlhelfern, was zumindest noch einmal mehr in die Breite geht.&lt;/p&gt;
&lt;p&gt;Die Fragen sind tatsächlich auch recht vielfältig gestellt und treffen diverse
Bereiche, wobei natürlich alle Bekannte wie z.Bsp. das Tempolimit fast überall
auftauchen.&lt;/p&gt;
&lt;p&gt;Ich kann (wie bei den anderen Wahlhelfen auch) auch hier eine klare Empfehlung
aussprechen.&lt;/p&gt;
&lt;h2 id=&quot;fazit&quot;&gt;Fazit&lt;/h2&gt;
&lt;p&gt;Wenn es nach mir geht, sollte jede/r Wähler/in bis zur Wahl mindestens einen -
besser aber direkt alle Wahlhelfer einmal gemacht haben, um eine Gefühl dafür zu
bekommen, welche Parteien für was genau stehen.&lt;/p&gt;
&lt;p&gt;Eines der dicksten Probleme (meiner Meinung nach) unserer Demokratie ist, dass
die Parteien und Politiker mittlerweile gelernt haben, wie sie reden können,
ohne verfängliche Aussagen zu treffen. Das verleitet viele Leute dazu, Parteien
zu wählen, die selbst für die eigenen Interessen absolut ungeeignet sind. Die
Wahlhelfer selber verändern diesen Fakt natürlich nicht, aber sie helfen dabei
eine Kompetenz über die Inhalte der Parteien zu bekommen, ohne dass es nötig ist
die Wahlprogramme zu lesen.&lt;/p&gt;
&lt;p&gt;Meine Empfehlung wäre also die folgende &quot;Formel&quot;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;alle Wahlhelfer durchspielen&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Parteien die sich konsequent im letzten Drittel bewegen auszuschließen&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Anschließend alle Wahlhelfer zu gewichten:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;1/3¹ der Bewertung von &lt;strong&gt;deinWahl&lt;/strong&gt;, da es das einzige Tool ist,
welches über (womöglich leere) Versprechungen hinaus geht.&lt;/p&gt;
&lt;p&gt;Solltet ihr tatsächlich auch kleine Parteien in betracht ziehen, empfehle
ich mit dem Wal zumindest ein Ausschlusskriterium zu bilden: Die zwei
schlechtesten Parteien würde ich ausschließen.&lt;/p&gt;
&lt;p&gt;¹ vorausgesetzt, ihr zieht sowieso nur die großen/etablierten Parteien in
Betracht&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;1/3 aus den &lt;strong&gt;anderen Helfen&lt;/strong&gt; generieren, indem wir jeder empfohlenen Partei
Punkte geben.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;1/3 dann auch selber aus Artikeln generieren. Dabei ist es wichtig, sich
weder von Tagesschau/ZDFHeute&amp;amp;Co noch (und das ist wichtig!) sich von
Facebook/Whatsapp-Gruppen/usw beeinflussen zu lassen.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Nehmt DIE Dinge, die euch wichtig sind (Rente, Steuern, Klima, was auch immer)
und befasst auch ausdrücklich auch mit den Gegenpositionen und Argumenten.
Damit stellen wir sicher, dass wir keine reine Bauchentscheidung für eine der
wichtigsten Wahlen der letzten Jahrzehnte treffen.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
</content>
        
    </entry>
    <entry xml:lang="de">
        <title>Klimakatastrophe I: Was ist</title>
        <published>2021-08-19T00:00:00+00:00</published>
        <updated>2021-08-26T07:15:00+00:00</updated>
        
        <author>
          <name>Unknown</name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://loki.dev/blog/klimakatastrophe-i-was-ist/"/>
        <id>https://loki.dev/blog/klimakatastrophe-i-was-ist/</id>
        
        <content type="html" xml:base="https://loki.dev/blog/klimakatastrophe-i-was-ist/">&lt;p&gt;&lt;em&gt;Zu allererst und am wichtigsten: Ich schreibe diesen Artikel für Familie und
Freunde mit dem Ziel, dass ich nicht ständig zum &quot;nervigen Klimaaktivsten&quot;
werden muss.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Es geht mir also darum, an einer Stelle möglichst viele Klimafakten zusammen zu
tragen und zu erklären, warum mir eine radikale Änderung von Politik und
Zusammenleben so wichtig ist.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Diese gesamte Problematik ist echt komplex und umfangreich, weswegen ich
versuche sie in 3 Artikel zu splitten&lt;/em&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Was ist (Dieser Artikel)&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Was kommt&lt;/em&gt; (und zwar in einer Zeit, die wir auf jeden Fall noch erleben werden
und Ophelia/Auroras Kernlebenszeit ausmacht)&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Was wir tun müssen&lt;/em&gt; (Nur der absolute wissenschaftliche/wirtschaftliche Konsens
und das Minimum um die Auswirkungen zu minimieren)&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;p&gt;Das abstrakte Konzept vom sogenannten Klimawandel (eigentlich Klimakatastrophe)
ist ja mittlerweile jedem bewusst und die groben Auswirkungen haben wir alle
schon einmal gehört: Meeresspiegel steigt, es wird heißer und auch die
Wahrscheinlichkeit für Extremwetterereignisse steigt.&lt;/p&gt;
&lt;p&gt;Aber das ist leider nicht das komplette Problem bzw. macht meiner Meinung die echten
Folgen nicht klar genug.&lt;/p&gt;
&lt;p&gt;In diesem ersten Beitrag möchte ich darüber reden, was für Auswirkungen wir
heute schon in Deutschland spüren und als ein Fernglas für Deutschland in die
Zukunft auch über das, was wir in der Welt jetzt schon sehen. Das sind alles
messbare Dinge und die allermeisten wurden wissenschaftlich exakt so
vorhergesagt. Leider meistens für ein paar Jahre später in der Zukunft. Denn
eines ist hier leider klar geworden: Fast alle Prognosen waren deutlich &lt;a rel=&quot;external&quot; href=&quot;https://www.hna.de/welt/klima-wandel-wetter-gletscher-schmilzt-katastrophale-folgen-hna-zr-90852117.html&quot;&gt;zu
optimistisch&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;folgen-in-deutschland&quot;&gt;Folgen in Deutschland&lt;/h2&gt;
&lt;p&gt;Am nächsten sind vielen wohl die Folgen direkt im eigenen Land. Kurz
stichpunktartig zusammengefasst:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Hitzetote haben sich verdoppelt&lt;/li&gt;
&lt;li&gt;Erste Seen sind bereits ausgetrocknet, andere von Blaualgen betroffen&lt;/li&gt;
&lt;li&gt;Wirtschaftliche Schäden in Milliardenhöhe&lt;/li&gt;
&lt;li&gt;Ernteausfälle&lt;/li&gt;
&lt;li&gt;Wasserknappheit&lt;/li&gt;
&lt;li&gt;Zoonosen&lt;/li&gt;
&lt;li&gt;Waldsterben&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;hitzetote&quot;&gt;Hitzetote&lt;/h3&gt;
&lt;p&gt;Seit 1955 ist die Anzahl der Hitzetoten um &lt;a rel=&quot;external&quot; href=&quot;https://www.tagesschau.de/inland/hitzetote-deutschland-103.html&quot;&gt;120%
gestiegen&lt;/a&gt;.
Das sind im 4 Jahreszeitraum 2014-2018 ungefähr 12000 gewesen. Ein Großteil dieses Wachstums ist
in der Zeit zwischen 2004 und 2014 - also dem letzten Zeitabschnitt passiert.
Um einen Vergleich zu bringen: Das sind &lt;a rel=&quot;external&quot; href=&quot;https://de.statista.com/statistik/daten/studie/185/umfrage/todesfaelle-im-strassenverkehr/&quot;&gt;1.5x mehr Tote als im gesamten
innerdeutschen
Verkehr&lt;/a&gt;.
Wobei das nicht ganz fair ist, da der Verkehr einen nicht unwesentlichen Beitrag
zu genau diesen Toten mit beiträgt.&lt;/p&gt;
&lt;h3 id=&quot;ausgetrocknete-seen-blaualgen&quot;&gt;Ausgetrocknete Seen &amp;amp; Blaualgen&lt;/h3&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https://www.morgenpost.de/vermischtes/article232506469/klimawandel-brandenburg-see-vertrocknet.html&quot;&gt;In
Brandenburg&lt;/a&gt;
sind bereits jetzt die ersten Seen ausgetrocknet. Wo früher ein See war ist jetzt
(noch) fruchtbare Wiese. Deutschlandweit sind viele weitere Seen betroffen bzw
werden in den nächsten 20 Jahren austrocknen, nur um dann alle paar Jahre von
Hochwasser gefüllt zu werden.&lt;/p&gt;
&lt;p&gt;Die Seen, die nicht austrocknen sind &lt;a rel=&quot;external&quot; href=&quot;https://www.ingenieur.de/technik/fachbereiche/umwelt/seen-im-klimawandel-forscher-entdecken-problem/&quot;&gt;bereits
jetzt&lt;/a&gt;
zunehmend im Sommer von Cyanobakterien betroffen. Ich erinnere mich noch an
meine Kindheit und wie wir bei Oppum im Baggersee schwimmen waren. Mittlerweile
wird vor Baggerseen &lt;a rel=&quot;external&quot; href=&quot;https://www.rnd.de/panorama/baden-in-seen-landesweit-zahlreiche-badeverbote-wegen-blaualgen-7RL2ZSEZVA76CDMS3T74CSESGQ.html&quot;&gt;zunehmend
gewarnt&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Um ganz ehrlich zu sein: Für die Bakterienbelastung ist die Klimatransformation
nicht die einzige Ursache. Aber das Thema der Landwirtschaft ist alleine für
sich so komplex und umfangreich, dass ich das in meinen Beiträgen hier erst
einmal weg lassen werde.&lt;/p&gt;
&lt;h3 id=&quot;wirtschaftlicher-schaden&quot;&gt;Wirtschaftlicher Schaden&lt;/h3&gt;
&lt;p&gt;Tatsächlich ein echt wichtiges Thema. Nicht weil ich das jetzt persönlich so
toll finde, sondern weil man ständig liest und hört, wer das alles bezahlen
soll bzw. dass die Politik (primär CDU/SPD) versuche, das Klima mit der
Wirtschaft in Einklang bringen will.&lt;/p&gt;
&lt;p&gt;Das halte ich für zutiefst unehrlich, da der Politik durch zahlreiche Berater
aus Wissenschaft &lt;strong&gt;und&lt;/strong&gt; Wirtschaft schon lange nahegelegt wird, dass
Klimaschutz auch Wirtschaftsschutz ist. Das einzige was leiden würde, wären
kurzfristige Quartalsergebnisse. Auf lange Sicht &lt;a rel=&quot;external&quot; href=&quot;https://www.goethe.de/prj/eco/de/bil/22136915.html&quot;&gt;ist Klimaschutz
billiger&lt;/a&gt; - Menschenleben
und Lebensqualität mal komplett ignoriert.&lt;/p&gt;
&lt;h3 id=&quot;mal-in-konkreten-zahlen&quot;&gt;Mal in konkreten Zahlen&lt;/h3&gt;
&lt;p&gt;Dass wir nicht nur nicht in den Klimaschutz investieren, sondern gerade die CDU
und insbesonderen Altmaier &lt;a rel=&quot;external&quot; href=&quot;https://www.tagesschau.de/wirtschaft/offshore-windenergie-ausbau-101.html&quot;&gt;aktiv den Klimaschutz
behindern&lt;/a&gt;,
macht es nicht leichter. So ist aus einer Industrie mit 80k Arbeitsplätzen der
regenerativen Energien, bei der wir weltweit Vorreiter waren, &lt;a rel=&quot;external&quot; href=&quot;https://www.windkraft-journal.de/2013/11/17/80-000-arbeitsplatzverluste-in-der-erneuerbare-energien-branche/45906?doing_wp_cron=1629527795.4246499538421630859375&quot;&gt;praktisch nichts
mehr
übrig&lt;/a&gt;.
Wenn selbst die &lt;a rel=&quot;external&quot; href=&quot;https://www.welt.de&quot;&gt;WELT&lt;/a&gt;, welche mit Klimathesen glänzt die
primär vom AFDP nahem &quot;EIKE Institut&quot; abgeschrieben wurden, &lt;a rel=&quot;external&quot; href=&quot;https://www.welt.de/wirtschaft/article198299117/Windindustrie-In-einem-Jahr-26-000-Arbeitsplaetze-abgebaut.html&quot;&gt;das
bemerkt&lt;/a&gt;,
dann ist wirklich Land unter.&lt;/p&gt;
&lt;h3 id=&quot;ernteausfalle&quot;&gt;Ernteausfälle&lt;/h3&gt;
&lt;p&gt;Es kann natürlich eine Anomalie gewesen sein, aber letztes Jahr (2020) lagen die
Ernteausfälle &lt;a rel=&quot;external&quot; href=&quot;https://www.deutschlandfunk.de/extremwetter-und-ernteausfaelle-klimawandel-stellt.724.de.html?dram:article_id=426057&quot;&gt;zwischen 30 und 50
Prozent&lt;/a&gt;.
Fast komplett durch die Dürre bedingt. Ein Faktor, der sich definitiv noch
zuspitzen wird. Dieses Jahr ist alles extrem feucht, sodass sowieso schon
geschwächte Pflanzen/Bäume/etc. direkt von der anderen Seite mit Fäule zu
kämpfen haben.&lt;/p&gt;
&lt;p&gt;Bisher wurden diese Umstände durch den Fakt abgefedert, dass wir sowieso schon
zu viel wegschmeißen bzw importieren konnten. Aber gerade unsere
Gemüse/Obst-Importländer wie z.Bsp. Spanien oder die Niederlande werden durch
Hitze (Spanien) oder Überflutungen (Niederlande) noch mehr zu kämpfen haben als
wir. Die Niederlande sind &lt;a rel=&quot;external&quot; href=&quot;https://www.tagesschau.de/ausland/europa/niederlande-klimawandel-103.html&quot;&gt;bereits
jetzt&lt;/a&gt;
unterhalb des Meeresspiegels, während sie sich gerade noch so &lt;em&gt;über Wasser&lt;/em&gt;
halten, &lt;a rel=&quot;external&quot; href=&quot;https://www.youthreporter.eu/de/beitrag/wenn-ein-land-unter-dem-meeresspiegel-liegt.10462/&quot;&gt;weil durchgehend Pumpen im Einsatz
sind&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;wasserknappheit&quot;&gt;Wasserknappheit&lt;/h3&gt;
&lt;p&gt;Letztes Jahr haben wir das in Nieder-Beerbach überdeutlich selber gemerkt, dass
einfach mal zwei Wochen Wassersparen angesagt war. In Niedersachsen gab es erste
Fälle von Wasserverteilung durch die Feuerwehr, weil die Becken einfach leer
sind.&lt;/p&gt;
&lt;p&gt;Durch unsere geographisch überaus günstige Lage wird es auch in der
mittelfristigen Zukunft nur um wenige Tage bis Wochen gehen und zusätzlich
örtlich begrenzt sein, aber die Auswirkungen werden trotzdem gravierend und
sorgen für &lt;a rel=&quot;external&quot; href=&quot;https://www.dw.com/de/wasserarmut-wasserknappheit-d%C3%BCrre-in-deutschland-nationaler-wasserdialog-wasserstrategie/a-56227882&quot;&gt;Warnungen von Wasserwerken und
Forschern&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;waldsterben&quot;&gt;Waldsterben&lt;/h3&gt;
&lt;p&gt;Zusätzlich zur Trinkwasserknappheit sind gerade 1/3 unserer Wälder dabei zu
sterben. Die Trockenheit alleine reicht häufig aus und wo diese nicht reicht
helfen &quot;Schädlinge&quot; dabei unseren Wäldern den Rest zu geben.&lt;/p&gt;
&lt;p&gt;Die Folgen sind nicht einfach nur &quot;weniger grün&quot; oder, dass wir weitere
CO&lt;sub&gt;2&lt;/sub&gt; Filter damit entfernen, sondern rapide abnehmende Biodiversität, Bodenschäden
(wie z.Bsp. Erdrutsche) und gravierendere Wetterphänomene, die nicht mehr durch Wälder geblockt oder
geschwächt werden.&lt;/p&gt;
&lt;p&gt;Eine Fläche von ehemals gut 12 Millionen Hektar hat in den letzten 20 Jahren mal
eben 300.000 Hektar schon eingebüßt - und da sind die Kohlestrom-Wälder noch
nicht eingerechnet. Ganz konkret &lt;a rel=&quot;external&quot; href=&quot;https://www.bmel.de/DE/themen/wald/wald-in-deutschland/waldzustandserhebung.html&quot;&gt;sind nur
~21%&lt;/a&gt;
unserer Bäume überhaupt noch gesund.&lt;/p&gt;
&lt;h3 id=&quot;zoonosen&quot;&gt;Zoonosen&lt;/h3&gt;
&lt;p&gt;Corona war nicht die erste Zoonose und wird nicht die letzte gewesen sein. Was
ohne menschlichen Einfluß eine Sache von Jahrhunderten war, wird häufiger
vorkommen. Sowohl meine Generation wird noch mindestens eine weitere Zoonose wie
Corona mitbekommen und für meine Töchter wird es Fall der Gewohnheit.&lt;/p&gt;
&lt;p&gt;Die &lt;strong&gt;Bundeszentrale für politische Bildung&lt;/strong&gt; selber &lt;a rel=&quot;external&quot; href=&quot;https://www.bpb.de/politik/innenpolitik/coronavirus/308483/pandemien-umwelt-und-klima&quot;&gt;geht auf ihren Seiten in
die Details
ein&lt;/a&gt;,
aber wie so häufig gilt dann doch eher, was RWE oder die heimische Stahl- und
Autoindustrie sich wünscht.&lt;/p&gt;
&lt;p&gt;Während wir also noch mit SARS-CoV2 kämpfen sind, gibt es schon weitere
Erreger diverser Art die auf dem Weg zu uns sind. Nicht nur, dass unsere
Bemühungen beim Klima/Menschenschutz zwischen mangelhaft und ungenügend sind,
treten wir als Menschheit noch einmal nach, indem wir primär durch unsere
&lt;a rel=&quot;external&quot; href=&quot;https://www.deutschlandfunkkultur.de/multiresistente-keime-in-der-landwirtschaft-die-naechste.976.de.html?dram:article_id=487266&quot;&gt;Fleisch- und Tierindustrie multiresistente Keime
züchten&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;weltweite-folgen&quot;&gt;Weltweite Folgen&lt;/h2&gt;
&lt;p&gt;Wenn wir vom Klimawandel reden, reden wir häufig auch von den s.g. Kipppunkten.
Davon haben wir bisher in etwa 16 identifiziert. Ein Kipppunkt ist eine Stelle,
ab der ein Prozess in Gang gesetzt wird, den wir &lt;strong&gt;nicht&lt;/strong&gt; wieder umkehren
können. Ab dieser Stelle läuft&#39;s halt und zwar in den meisten Fälle mit
selbstverstärkender Wirkung.&lt;/p&gt;
&lt;h3 id=&quot;permafrost&quot;&gt;Permafrost&lt;/h3&gt;
&lt;p&gt;Einen der Kipppunkte die wir bereits erreicht haben, ist dass Weltweit &lt;a rel=&quot;external&quot; href=&quot;https://www.n-tv.de/wissen/Permafrostboeden-weltweit-erwaermen-sich-article20813171.html&quot;&gt;der
Permafrost&lt;/a&gt;
auftaut. Dabei werden große Mengen Methan freigegeben, was die Erwärmung
natürlich zusätzlich weiter beschleunigt. Methan ist schon am ehesten das, was
uns nachhaltig schadet, aber auftauender Permafrost hat noch deutlich mehr
Probleme.&lt;/p&gt;
&lt;p&gt;So berichtet zum Beispiel &lt;a rel=&quot;external&quot; href=&quot;https://www.geo.de/natur/oekologie/23609-rtkl-gefahr-aus-dem-eis-viren-und-bakterien-werden-durch-den-auftauenden&quot;&gt;die online Variante des
Geomagazins&lt;/a&gt;
von mehreren Studien, die über weitere Bakterien, Viren und mehr berichten, die
sich dort verstecken.
Das allermeiste wird harmlos sein, aber selbst harmlose 15.000 Jahre alte
Erreger können unser Immunsystem extrem empfindlich treffen.&lt;/p&gt;
&lt;p&gt;Ein echtes Beispiel gab es zum Beispiel bei der Übersiedelung nach Amerika, wo
europäische Kolonisten ihre Krankheiten mitbrachten und ganze Stämme
auslöschten. Nicht weil diese Krankheiten an sich so gefährlich waren, sondern
weil die Immunsysteme der American Natives nicht vorbereitet waren.&lt;/p&gt;
&lt;h3 id=&quot;regenwald&quot;&gt;Regenwald&lt;/h3&gt;
&lt;p&gt;Der Regenwald wurde und wird fälschlicherweise noch als &lt;a rel=&quot;external&quot; href=&quot;https://www.regenwald.org/themen/der-regenwald&quot;&gt;die grüne Lunge der
Erde&lt;/a&gt; bezeichnet. Leider haben
wir dieses Jahr den Punkt erreicht, wo aus dem Regenwald &lt;strong&gt;mehr&lt;/strong&gt;(!)
Treibhausgase austreten, &lt;a rel=&quot;external&quot; href=&quot;https://www.frontiersin.org/articles/10.3389/ffgc.2021.618401/full&quot;&gt;als er
absorbiert&lt;/a&gt;.
Das ist extrem bitter und ist in diesem Fall zu fast &lt;a rel=&quot;external&quot; href=&quot;https://www.faszination-regenwald.de/info-center/zerstoerung/&quot;&gt;100% auf
Fleischkonsum&lt;/a&gt;
zurück zu führen.&lt;/p&gt;
&lt;p&gt;Um es noch einmal deutlich zu sagen/schreiben: Wir haben dieses Jahr den Punkt
erreicht, wo der Regenwald NICHT mehr die grüne Lunge ist. Ganz trivial bedeutet
das, dass wir unsere Schulbücher umschreiben müssen, praktisch heißt das, dass
wir eines der wichtigsten Ökosysteme des Planeten nachhaltig vernichtet haben.
Und das für stark veredeltes Soja?&lt;/p&gt;
&lt;p&gt;Ironischerweise wird hier auf die Sojafelder und böse Veggies verwiesen, dass
vom weltweiten Soja nur 6% in diversen Veggieprodukten landet und der Rest zu
Tierfutter verarbeitet wird, wird gerne verschwiegen.
Vom Regenwaldsoja landet auf direktem Wege in deutschen Mägen sowieso praktisch
nichts, da hierfür die Produkte halbwegs regionale Erzeuger verwendet werden. So
braucht eine Kuh rund 12kg Soja um ein Kilo Fleisch zu erzeugen.
Schwein/Geflügel ist da zum Glück deutlich genügsamer.&lt;/p&gt;
&lt;h2 id=&quot;aber-wir-konnen-doch-nicht-die-welt-retten&quot;&gt;Aber wir können doch nicht die Welt retten&lt;/h2&gt;
&lt;p&gt;... wie bewegen wir die anderen dazu mitzuhelfen?&lt;/p&gt;
&lt;p&gt;Müssen wir nicht. Wir müssen hauptsächlich einmal anfangen, nicht mehr zu
bremsen. In Europa sind wir &lt;a rel=&quot;external&quot; href=&quot;https://www.germanwatch.org/de/15938&quot;&gt;DIE&lt;/a&gt;
&lt;a rel=&quot;external&quot; href=&quot;https://www.sueddeutsche.de/politik/klimawandel-klimaschutz-deutschland-1.4164096&quot;&gt;Bremse&lt;/a&gt;
&lt;a rel=&quot;external&quot; href=&quot;https://www.tagesspiegel.de/politik/vertrauliche-dokumente-deutschland-wird-zum-klima-bremser/24134082.html&quot;&gt;schlechthin&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Obwohl wir nur 1% der Weltbevölkerung stellen erzeugen wir rund 2% der
Klimagase. Da sind in China/Taiwan produzierte Produkte oder Diensleistungen wie
Server&amp;amp;Co aus dem Ausland, die für den deutschen Markt bestimmt sind und uns
eigentlich zugerechnet werden &lt;em&gt;müssten&lt;/em&gt;, noch nicht einberechnet.&lt;/p&gt;
&lt;p&gt;Wir sind in Deutschland pro Kopf noch deutlich vor China, welches bei rund 5t
CO&lt;sub&gt;2&lt;/sub&gt; liegt, während wir bei durchschnittlich 11t sind. Doppelt so viel
wie das schlimme China. Bei über 200 Ländern sind wir momentan mit dem absoluten
Ausstaß unter den Top10. Wollen wir wirklich, dass fast 200 Länder mit der
gleichen Argumentation kommen? Würden wir unseren Kindern &quot;aber die andern!&quot; als
Argument durchlassen?&lt;/p&gt;
&lt;p&gt;Edit: Abschließend bleibt hier noch einmal diese Wunderbare Zusammenfassung &lt;a rel=&quot;external&quot; href=&quot;https://www.quarks.de/umwelt/klimawandel/so-steht-es-wirklich-um-unser-klima/&quot;&gt;von
Quarks&lt;/a&gt;
des ersten Teilberichts vom IPCC. Insgesamt wird es 3 Berichte und 1
Zusammenfassung geben. Wichtig hierbei ist, dass die Berichte schon fertig sind,
aber erst 2022 veröffentlicht werden, weil die Regierungen noch einmal Einfluss
nehmen können. Eine Zusammenfassung des Leaks aus Teil 2 (sollte erst Januar
2022 kommen) hat &lt;a rel=&quot;external&quot; href=&quot;https://www.spiegel.de/wissenschaft/mensch/geleakter-teil-des-ipcc-berichts-die-reichsten-zehn-prozent-verursachen-mehr-als-ein-drittel-der-treibhausgase-a-6d2e8438-7f3e-49bc-8967-4e3aeb6be585&quot;&gt;der
Spiegel&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Relevant ist hier vielleicht auch, dass der GRUND für diesen Leak ist, dass die
Wissenschaftler &lt;a rel=&quot;external&quot; href=&quot;https://utopia.de/news/leaks-des-ipcc-berichts-die-reichsten-10-prozent-verursachen-ueber-36-prozent-der-treibhausgase/&quot;&gt;Angst
haben&lt;/a&gt;,
dass der Bericht &lt;a rel=&quot;external&quot; href=&quot;https://www.theguardian.com/environment/2021/aug/12/greenhouse-gas-emissions-must-peak-within-4-years-says-leaked-un-report&quot;&gt;von den
Regierungen&lt;/a&gt;
verwässert wird.&lt;/p&gt;
</content>
        
    </entry>
    <entry xml:lang="de">
        <title>whoami</title>
        <published>2021-08-17T00:00:00+00:00</published>
        <updated>2021-08-17T00:00:00+00:00</updated>
        
        <author>
          <name>Unknown</name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://loki.dev/about/"/>
        <id>https://loki.dev/about/</id>
        
        <content type="html" xml:base="https://loki.dev/about/">&lt;h1 id=&quot;whoami&quot;&gt;#whoami&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;Vater&lt;/li&gt;
&lt;li&gt;Ehemann&lt;/li&gt;
&lt;li&gt;Entwickler&lt;/li&gt;
&lt;li&gt;Wissenschaftsfreak&lt;/li&gt;
&lt;li&gt;Klimafreundlich (Familie endlich seit 2021 naiv CO&lt;sub&gt;2&lt;/sub&gt; neutral)&lt;/li&gt;
&lt;/ul&gt;
</content>
        
    </entry>
</feed>
