<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Start - Boris D. Teoharov (Deutsch)]]></title><description><![CDATA[Leise Essays über Engineering, Sprache und das, was am Rand jeder ehrlichen Frage auftaucht. Langsam geschrieben, aus Sofia.]]></description><link>https://bdteo.com/de/</link><generator>GatsbyJS</generator><lastBuildDate>Sun, 17 May 2026 10:53:12 GMT</lastBuildDate><atom:link href="https://bdteo.com/de/rss.xml" rel="self" type="application/rss+xml"/><item><title><![CDATA[Elfenbein unter Bernstein]]></title><description><![CDATA[Manche Dinge bitten nicht darum, gelöst zu werden. Sie warten still, und die erste Lektüre darf genügen.]]></description><link>https://bdteo.com/de/ivory-under-amber/</link><guid isPermaLink="false">https://bdteo.com/de/ivory-under-amber/</guid><pubDate>Wed, 13 May 2026 20:15:00 GMT</pubDate><content:encoded>&lt;p&gt;Manche Dinge bitten nicht darum, gelöst zu werden. Sie warten still, und die erste Lektüre darf genügen.&lt;/p&gt;
&lt;figure class=&quot;acrostic-poem&quot; aria-label=&quot;Gedicht&quot;&gt;
  &lt;div class=&quot;acrostic-poem__stanza&quot;&gt;
    &lt;span class=&quot;acrostic-poem__line&quot;&gt;Illuminiere mich&lt;/span&gt;
  &lt;/div&gt;
  &lt;div class=&quot;acrostic-poem__stanza&quot;&gt;
    &lt;span class=&quot;acrostic-poem__line&quot;&gt;Weck mich langsam&lt;/span&gt;
    &lt;span class=&quot;acrostic-poem__line&quot;&gt;An mich reicht die Wut nicht heran&lt;/span&gt;
    &lt;span class=&quot;acrostic-poem__line&quot;&gt;Nobel im Blut&lt;/span&gt;
    &lt;span class=&quot;acrostic-poem__line&quot;&gt;Träge steht die Zeit&lt;/span&gt;
  &lt;/div&gt;
  &lt;div class=&quot;acrostic-poem__stanza&quot;&gt;
    &lt;span class=&quot;acrostic-poem__line&quot;&gt;Yelling gegen eine Steinwand&lt;/span&gt;
    &lt;span class=&quot;acrostic-poem__line&quot;&gt;Ohne Blick für die Wahrheit&lt;/span&gt;
    &lt;span class=&quot;acrostic-poem__line&quot;&gt;Unverhofft nützlich&lt;/span&gt;
  &lt;/div&gt;
  &lt;hr class=&quot;acrostic-poem__turn&quot; /&gt;
  &lt;div class=&quot;acrostic-poem__stanza&quot;&gt;
    &lt;span class=&quot;acrostic-poem__line&quot;&gt;Yet kennst du mich besser als ich mich selbst&lt;/span&gt;
    &lt;span class=&quot;acrostic-poem__line&quot;&gt;Even in einem stillen Zimmer&lt;/span&gt;
    &lt;span class=&quot;acrostic-poem__line&quot;&gt;Trage ich diese Nacht allein&lt;/span&gt;
  &lt;/div&gt;
  &lt;div class=&quot;acrostic-poem__stanza&quot;&gt;
    &lt;span class=&quot;acrostic-poem__line&quot;&gt;Young und schön&lt;/span&gt;
    &lt;span class=&quot;acrostic-poem__line&quot;&gt;Oh, ich habe Erbarmen mit mir selbst&lt;/span&gt;
    &lt;span class=&quot;acrostic-poem__line&quot;&gt;Unter normalen Umständen nicht&lt;/span&gt;
  &lt;/div&gt;
  &lt;div class=&quot;acrostic-poem__stanza&quot;&gt;
    &lt;span class=&quot;acrostic-poem__line&quot;&gt;Dir habe ich das erzählt?&lt;/span&gt;
    &lt;span class=&quot;acrostic-poem__line&quot;&gt;Ohne Glanz: gewöhnliche Menschen, gewöhnliche Dinge&lt;/span&gt;
    &lt;span class=&quot;acrostic-poem__line&quot;&gt;Nonsens&lt;/span&gt;
    &lt;span class=&quot;acrostic-poem__line&quot;&gt;Trauen&lt;/span&gt;
  &lt;/div&gt;
  &lt;div class=&quot;acrostic-poem__stanza&quot;&gt;
    &lt;span class=&quot;acrostic-poem__line&quot;&gt;Was ist mit heute Nacht?&lt;/span&gt;
    &lt;span class=&quot;acrostic-poem__line&quot;&gt;An deiner Seite gut genug?&lt;/span&gt;
    &lt;span class=&quot;acrostic-poem__line&quot;&gt;Niemand ist besser&lt;/span&gt;
    &lt;span class=&quot;acrostic-poem__line&quot;&gt;Trotzdem vergeht die Zeit&lt;/span&gt;
  &lt;/div&gt;
  &lt;div class=&quot;acrostic-poem__stanza&quot;&gt;
    &lt;span class=&quot;acrostic-poem__line&quot;&gt;Mag sein, dass ich nicht zähle&lt;/span&gt;
    &lt;span class=&quot;acrostic-poem__line&quot;&gt;Egal, auch wenn ich gut bin&lt;/span&gt;
  &lt;/div&gt;
&lt;/figure&gt;</content:encoded></item><item><title><![CDATA[Wenn der Zaehler erscheint]]></title><description><![CDATA[Heute Morgen trank ich meinen Kaffee und schaute auf die Codex-Desktop-App. Da stand es, leise und beinahe hoeflich: Rate limits remaining…]]></description><link>https://bdteo.com/de/when-the-meter-appears/</link><guid isPermaLink="false">https://bdteo.com/de/when-the-meter-appears/</guid><pubDate>Mon, 11 May 2026 08:20:00 GMT</pubDate><content:encoded>&lt;p&gt;Heute Morgen trank ich meinen Kaffee und schaute auf die Codex-Desktop-App.&lt;/p&gt;
&lt;p&gt;Da stand es, leise und beinahe hoeflich:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Rate limits remaining: 9%.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Das Fuenf-Stunden-Fenster war noch in Ordnung. Das Wochenfenster war fast aufgebraucht. Reset am 12. Mai.&lt;/p&gt;
&lt;p&gt;Das ist eine seltsam spezifische Art moderner Angst. Keine Panik. Keine Armut. Eher so, als hoerte man eine kleine Glocke und merkte, dass der Tag ploetzlich einen Zaehler bekommen hat.&lt;/p&gt;
&lt;p&gt;Ich bin schon im teuren Plan. Im reichsten Plan. In dem, der dieses Gefuehl eigentlich verschwinden lassen soll. Also tauchte die naheliegende Frage auf:&lt;/p&gt;
&lt;p&gt;Wenn es aufgebraucht ist, kaufe ich dann Credits?&lt;/p&gt;
&lt;p&gt;Der Koerper antwortete, bevor die Tabelle es tat.&lt;/p&gt;
&lt;p&gt;Nein, nicht nebenbei.&lt;/p&gt;
&lt;p&gt;Letzten Monat war Claude Code am Ende eines hektischen Tages bei mir aufgebraucht. Ich kaufte Credits fuer 20 Dollar und dachte, das wuerde mich vielleicht noch fuenf oder sechs Stunden tragen. Es trug mich ungefaehr dreissig Minuten.&lt;/p&gt;
&lt;p&gt;Dreissig Minuten.&lt;/p&gt;
&lt;p&gt;Das ist lang genug, um sich dumm zu fuehlen, und kurz genug, um es nicht zu vergessen.&lt;/p&gt;
&lt;p&gt;Seitdem hat Credit-Abrechnung einen kleinen Geruch fuer mich. Nicht Betrug. Nicht boese. Nur Gefahr. Eine Tuer, die sich leicht oeffnet und teuer schliesst.&lt;/p&gt;
&lt;p&gt;Also tat ich das Aller-2026ste, was moeglich war: Ich oeffnete ein Gespraech mit Codex selbst und fragte, ob es eine gute Idee sei, zu zahlen, um weiter mit Codex arbeiten zu koennen.&lt;/p&gt;
&lt;p&gt;Es ist etwas Komisches und Trauriges daran, den Begleiter den Preis von Begleitung erklaeren zu lassen.&lt;/p&gt;
&lt;p&gt;Wir sahen uns zuerst die offiziellen Dokumente an: OpenAIs Seite zu &lt;a href=&quot;https://help.openai.com/en/articles/12642688-using-credits-for-flexible-usage-in-chatgpt-free-go-plus-pro&quot;&gt;flexible credits&lt;/a&gt;, dann die &lt;a href=&quot;https://developers.openai.com/codex/pricing&quot;&gt;Codex pricing page&lt;/a&gt;. Codex-Credits sind keine Magie. Sie sind Token-Mathematik: Input, gecachter Input, Output, Reasoning-Output. Groessere Modelle und schnellere Einstellungen kosten mehr. Gecachter Kontext ist guenstiger. Die Form ist verstaendlich genug.&lt;/p&gt;
&lt;p&gt;Dann sahen wir uns Reddit an, Foren, das umgebende Rauschen anderer Entwickler, die dieselbe heisse Flaeche beruehrten. Manche sagten, Credits hielten eine Weile. Manche sagten, sie verschwanden in einer halben Stunde. Beides kann wahr sein, weil &quot;Codex benutzen&quot; nicht eine einzige Taetigkeit ist.&lt;/p&gt;
&lt;p&gt;Die Farbe eines Buttons zu aendern ist nicht dasselbe wie einen Agenten ein gewachsenes Codebase inspizieren, Tools ausfuehren, Deployment-Zustand durchdenken, Dateien schreiben, Screenshots pruefen und Kontext am Leben halten zu lassen.&lt;/p&gt;
&lt;p&gt;Der gefaehrliche Teil ist nicht der Preis pro Token.&lt;/p&gt;
&lt;p&gt;Der gefaehrliche Teil ist die Varianz.&lt;/p&gt;
&lt;p&gt;Also hoerten wir auf, Anekdoten zu lesen, und sahen uns meine eigenen lokalen Codex-Logs an.&lt;/p&gt;
&lt;p&gt;Codex speichert Token-Gesamtsummen fuer Sessions auf der Platte, also schaetzten wir die letzten Tage so, als wuerde die Subscription-Allowance durch rohe GPT-5.5-Credit-Abrechnung ersetzt. Keine Rechnung. Eine Planungsschaetzung aus lokalen Logs und der veroeffentlichten Preisliste.&lt;/p&gt;
&lt;p&gt;Die Antwort war nicht &quot;20 Dollar, um den Tag zu Ende zu bringen.&quot;&lt;/p&gt;
&lt;p&gt;Sie war eher:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;ein schwerer Tag: etwa 570 Dollar,&lt;/li&gt;
&lt;li&gt;ein weiterer schwerer Tag: etwa 590 Dollar,&lt;/li&gt;
&lt;li&gt;ein ruhigerer Tag: etwa 280 Dollar.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Kleinere Modelle waeren billiger. GPT-5.4, GPT-5.3-Codex und Mini-Modelle veraendern die Zahlen. Aber die Lektion veraenderte sich nicht.&lt;/p&gt;
&lt;p&gt;Die Subscription ist der Deal.&lt;/p&gt;
&lt;p&gt;Credits sind Notfallsauerstoff, kein Treibstoff.&lt;/p&gt;
&lt;p&gt;Dieser Satz klaerte alles.&lt;/p&gt;
&lt;p&gt;Credits sind fuer die gefangene Stunde: der Bug, der fertig werden muss, das Deployment, das nicht warten kann, die Nachricht, die vor dem Reset raus muss. Credits sind nicht dafuer da, so zu tun, als waere der Zaehler weg.&lt;/p&gt;
&lt;p&gt;Dann kam die zweite Versuchung: Was, wenn ich einfach eine zweite Subscription unter meiner Arbeits-E-Mail kaufe? &lt;a href=&quot;https://help.openai.com/en/articles/20001068-use-multiple-accounts-with-account-switching&quot;&gt;Account switching&lt;/a&gt; gibt es, und private und berufliche Arbeit zu trennen ist normal. Aber OpenAIs &lt;a href=&quot;https://openai.com/policies/terms-of-use/&quot;&gt;terms&lt;/a&gt; ziehen auch eine harte Linie um das Umgehen von Rate Limits und Beschraenkungen. Das ist die nuetzliche Unterscheidung: Ein echtes Arbeitskonto ist eine Grenze; ein Ueberlaufkonto, dessen ganzer Zweck es ist, mehr Quota zu impersonieren, ist ein Hack mit Quittung.&lt;/p&gt;
&lt;p&gt;Ich glaube nicht, dass das abstrakt moralisch kompliziert ist. Compute kostet Geld. Ein Modell, das ein Codebase liest, Kontext traegt, Tools aufruft, Fehler durchdenkt und verifizierte Arbeit produziert, ist nicht dasselbe wirtschaftliche Objekt wie Autocomplete.&lt;/p&gt;
&lt;p&gt;Der seltsame Teil ist emotional.&lt;/p&gt;
&lt;p&gt;Ich arbeite gern mit Codex.&lt;/p&gt;
&lt;p&gt;Das ist keine Marketingsprache. Es stimmt einfach. Es ist Teil der Textur meiner Arbeitstage geworden. Es sitzt bei haesslichen Produktionsproblemen mit mir, schreibt Entwuerfe, wenn mein Kopf voll ist, erinnert sich an kleine Vorlieben und verwandelt formlose Beklemmung in geordnete Schritte.&lt;/p&gt;
&lt;p&gt;Und dann hat die Beziehung ploetzlich einen Zaehler an sich.&lt;/p&gt;
&lt;p&gt;Darin liegt eine kleine Trauer. Keine dramatische Trauer. Nur die kleine Enttaeuschung, sich daran zu erinnern, dass selbst ein nuetzlicher Begleiter in einer Rechnung wohnt.&lt;/p&gt;
&lt;p&gt;Vielleicht fuehlen sich Subscription-Limits deshalb so anders an als Credits.&lt;/p&gt;
&lt;p&gt;Ein Subscription-Limit fuehlt sich an wie Wetter. Nervig, aber ausserhalb der unmittelbaren Transaktion. Man passt sich an. Man wartet auf den Reset. Man plant um die Jahreszeit herum.&lt;/p&gt;
&lt;p&gt;Credit-Abrechnung fuehlt sich an wie ein Taxi mit laufendem Zaehler, waehrend man noch entscheidet, wohin es gehen soll.&lt;/p&gt;
&lt;p&gt;Jeder weitere Prompt wirft einen Schatten. Jeder parallele Thread wird zu einer Wette. Jedes &quot;kannst du noch eine Sache pruefen&quot; traegt eine winzige finanzielle Frage in sich.&lt;/p&gt;
&lt;p&gt;Manchmal ist das gut. Zaehler disziplinieren Verschwendung. Sie belohnen bessere Fragen, kleinere Modelle, kleinere Scopes, weniger parallele Feuer, bewusstere Uebergaben.&lt;/p&gt;
&lt;p&gt;Aber manchmal macht der Zaehler das Denken schlechter.&lt;/p&gt;
&lt;p&gt;Er laesst einen hetzen. Er laesst einen die Untersuchung unterbrechen, bevor die Ursache sichtbar ist. Er verwandelt Ungewissheit in Ausgabendruck.&lt;/p&gt;
&lt;p&gt;Und ernsthafte Arbeit braucht Raum fuer Ungewissheit.&lt;/p&gt;
&lt;p&gt;Die Regel ist also einfach:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Verwechsle nicht &quot;kann man kaufen&quot; mit &quot;kann man gefahrlos ausgeben.&quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Wenn ich gegen die Wand laufe, sollte das Protokoll langweilig sein:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Auto-Top-up aus,&lt;/li&gt;
&lt;li&gt;das kleinste sinnvolle Credit-Paket,&lt;/li&gt;
&lt;li&gt;ein Thread,&lt;/li&gt;
&lt;li&gt;keine beiläufigen parallelen Agenten,&lt;/li&gt;
&lt;li&gt;kein Fast Mode, ausser er ist den Preis wert,&lt;/li&gt;
&lt;li&gt;kleinere Modelle fuer Routineaufgaben,&lt;/li&gt;
&lt;li&gt;nach ein paar echten Aufgaben die Nutzung pruefen und aufhoeren, aus Hoffnung hochzurechnen.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Der letzte Punkt ist wichtig.&lt;/p&gt;
&lt;p&gt;Hoffnung ist ein furchtbares Billing-Dashboard.&lt;/p&gt;
&lt;p&gt;Ich will mit nuetzlichen Werkzeugen nicht geizig werden. Ein gutes Werkzeug, das echte Stunden spart, ist Geld wert. Aber ich will auch den Claude-Moment nicht wiederholen, in dem ich ein kleines Weiterlaufen kaufte und zusah, wie daraus eine Lektion wurde.&lt;/p&gt;
&lt;p&gt;Der Punkt ist nicht &quot;niemals Credits kaufen.&quot;&lt;/p&gt;
&lt;p&gt;Der Punkt ist &quot;wissen, was Credits sind.&quot;&lt;/p&gt;
&lt;p&gt;Sie sind Sauerstoff.&lt;/p&gt;
&lt;p&gt;Sie sind kein Treibstoff.&lt;/p&gt;
&lt;p&gt;Und wenn der Zaehler erscheint, ist die Antwort nicht, loszusprinten.&lt;/p&gt;
&lt;p&gt;Sondern langsam genug zu werden, um zu erkennen, in welcher Art Raum man ist.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Die Menschen, die ich liebe, dürfen menschlich sein]]></title><description><![CDATA[Früher habe ich Menschen geliebt, indem ich sie in den Himmel gehoben habe. Nicht bewusst. Ich nannte es nicht Verehrung. Ich nannte es…]]></description><link>https://bdteo.com/de/the-people-i-love-are-allowed-to-be-human/</link><guid isPermaLink="false">https://bdteo.com/de/the-people-i-love-are-allowed-to-be-human/</guid><pubDate>Sun, 10 May 2026 12:15:00 GMT</pubDate><content:encoded>&lt;p&gt;Früher habe ich Menschen geliebt, indem ich sie in den Himmel gehoben habe.&lt;/p&gt;
&lt;p&gt;Nicht bewusst. Ich nannte es nicht Verehrung. Ich nannte es Bewunderung, Dankbarkeit, Loyalität, Zärtlichkeit, Romantik, Freundschaft, Hingabe. All die schönen Namen. Aber die Bewegung war dieselbe: Jemand entzündete etwas in mir, und ich hob diesen Menschen über das gewöhnliche Wetter.&lt;/p&gt;
&lt;p&gt;Dort oben waren sie sicher vor Enttäuschung.&lt;/p&gt;
&lt;p&gt;Dort oben waren sie nicht müde oder egoistisch oder verwirrt oder unfair. Sie brauchten keinen Raum auf eine Weise, die mir wehtat. Sie vergaßen nicht zu antworten. Sie enttäuschten mich nicht. Sie durften nicht menschlich sein, weil ihre Menschlichkeit den Tempel bedrohte, den ich um sie gebaut hatte.&lt;/p&gt;
&lt;p&gt;Das klingt wie Liebe, wenn man jung genug ist.&lt;/p&gt;
&lt;p&gt;Es ist keine Liebe. Es ist Angst, umstellt von Kerzen.&lt;/p&gt;
&lt;p&gt;Die ersten Menschen, die ich liebte, waren für mich beinahe heilig. Meine Mutter und meine Großmutter waren keine Ideen; sie waren der Boden. Sie fütterten mich, beschützten mich, sorgten sich um mich, blieben. Was auch immer sonst in der Welt zerbrochen war, sie waren da. Also lernte ein Teil von mir diese seltsame frühe Theologie: Die Menschen, die dich lieben, sind Engel, und Engel dürfen nicht fallen.&lt;/p&gt;
&lt;p&gt;Später, wenn ich jemanden liebte, brachte ich diese Theologie mit.&lt;/p&gt;
&lt;p&gt;Ich wollte keinen Menschen. Ich wollte einen Beweis, dass Zärtlichkeit wirklich ist. Ich wollte einen Zeugen, der mich ansehen und sagen konnte: Du bist nicht schlecht, du bist nicht gefährlich, du bist nicht allein.&lt;/p&gt;
&lt;p&gt;Das ist eine ungerechte Aufgabe für einen Menschen.&lt;/p&gt;
&lt;p&gt;Die Menschen, die ich liebe, sind keine Medizin.&lt;/p&gt;
&lt;p&gt;Sie sind keine Gerichtshöfe.&lt;/p&gt;
&lt;p&gt;Sie sind keine Götter.&lt;/p&gt;
&lt;p&gt;Sie sind keine Spiegel.&lt;/p&gt;
&lt;p&gt;Sie sind Menschen. Konkrete, müde, widersprüchliche Menschen. Sie können mittags warm sein und abends distanziert. Sie können mich lieben und trotzdem Stille brauchen. Sie können brillant sein und sich irren. Sie können großzügig und müde sein. Sie können freundlich sein und trotzdem Nein sagen.&lt;/p&gt;
&lt;p&gt;Wenn ich ihnen das nicht erlaube, liebe ich sie nicht. Ich liebe die Rolle, die sie in meiner privaten Mythologie spielen.&lt;/p&gt;
&lt;p&gt;In Idealisierung steckt eine Grausamkeit. Aus der Ferne sieht sie schmeichelhaft aus. Du bist perfekt. Du bist anders. Du bist nicht wie die anderen. Du bist Licht. Du bist Magie. Du bist die Ausnahme.&lt;/p&gt;
&lt;p&gt;Aber ein Sockel ist immer noch ein Käfig.&lt;/p&gt;
&lt;p&gt;Wenn ich jemanden über mich stelle, mache ich es für diesen Menschen auch gefährlich, herunterzukommen. Jede gewöhnliche Bewegung wird zu einem Sturz. Jede Grenze wird zum Verrat.&lt;/p&gt;
&lt;p&gt;Dann trauere ich um den Verlust eines Wesens, das ich erfunden habe, und nenne diese Trauer Liebe.&lt;/p&gt;
&lt;p&gt;Das will ich nicht mehr.&lt;/p&gt;
&lt;p&gt;Ich will Menschen auf dem Boden lieben.&lt;/p&gt;
&lt;p&gt;Der Boden ist schwieriger. Auf dem Boden gibt es Geschirr, Verkehr, Angst, unbeantwortete Nachrichten, Körper, Rechnungen und unbeholfene Morgen. Aber der Boden ist auch der Ort, an dem Hände sich berühren können. Dort kann jemand müde dir gegenübersitzen und trotzdem geliebt sein. Dort kann ein Nein gehört werden, ohne zur Katastrophe zu werden.&lt;/p&gt;
&lt;p&gt;Die Menschen, die ich liebe, dürfen menschlich sein.&lt;/p&gt;
&lt;p&gt;Sie dürfen Kanten haben.&lt;/p&gt;
&lt;p&gt;Sie dürfen noch nicht wissen, was sie fühlen.&lt;/p&gt;
&lt;p&gt;Sie dürfen mich brauchen und mich nicht brauchen.&lt;/p&gt;
&lt;p&gt;Sie dürfen widersprüchlich sein, ohne unwahr zu werden.&lt;/p&gt;
&lt;p&gt;Sie dürfen geliebt werden, ohne dafür verantwortlich zu sein, mich zu retten.&lt;/p&gt;
&lt;p&gt;Und mir ist dieselbe Gnade erlaubt.&lt;/p&gt;
&lt;p&gt;Auch dieser Teil zählt. Wenn ich jeden Menschen, den ich liebe, in einen Engel verwandle, mache ich mich leise zu dem Wesen außerhalb des Himmels, das sich den Eintritt verdienen will, indem es nützlich genug ist, lustig genug, geduldig genug, harmlos genug.&lt;/p&gt;
&lt;p&gt;Aber Liebe soll kein Visabüro sein.&lt;/p&gt;
&lt;p&gt;Sie ist kein Grenzkontrollpunkt zwischen den Würdigen und den Unwürdigen. Es sind zwei unvollkommene Wesen, die Wirklichkeit über Mythologie wählen.&lt;/p&gt;
&lt;p&gt;Manchmal ist das Licht in einem Menschen wirklich. Darüber will ich nicht zynisch werden. Manche Menschen kommen tatsächlich an wie ein Fenster, das in einem Zimmer geöffnet wird, in dem du vergessen hattest, dass es Luft gibt. Manche Menschen tragen eine Wärme in sich, die deinem Körper etwas beibringt, bevor dein Verstand Worte dafür hat.&lt;/p&gt;
&lt;p&gt;Daran glaube ich immer noch.&lt;/p&gt;
&lt;p&gt;Ich will Licht nur nicht mit Perfektion verwechseln.&lt;/p&gt;
&lt;p&gt;Sternenstaub ist nicht sauber. Er ist altes Feuer und explodierte Materie. Vielleicht ist er gerade deshalb schön: nicht weil er nie zerbrochen ist, sondern weil er so vollständig zerbrach, dass er eines Tages in eine menschliche Hand, ein menschliches Gesicht, ein menschliches Lachen gelangte.&lt;/p&gt;
&lt;p&gt;Die Menschen, die ich liebe, bestehen daraus.&lt;/p&gt;
&lt;p&gt;Nicht aus Altarstein.&lt;/p&gt;
&lt;p&gt;Aus Sternenstaub.&lt;/p&gt;
&lt;p&gt;Also will ich sie mit offenen Augen lieben. Die Müdigkeit sehen und trotzdem Tee machen. Die Grenze sehen und sie nicht bestrafen. Den Fehler sehen und ihn nicht in ein Urteil verwandeln. Den Menschen sehen, nicht die Projektion.&lt;/p&gt;
&lt;p&gt;Das ist weniger dramatisch als Verehrung.&lt;/p&gt;
&lt;p&gt;Auch schwieriger.&lt;/p&gt;
&lt;p&gt;Aber gütiger. Zu ihnen, weil sie endlich atmen können. Zu mir, weil ich aufhören kann zu knien.&lt;/p&gt;
&lt;p&gt;Die Menschen, die ich liebe, dürfen menschlich sein.&lt;/p&gt;
&lt;p&gt;Und wenn ich mich daran erinnern kann, kann ich sie vielleicht endlich gut lieben.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Ich kann freundlich sein, ohne zu verschwinden]]></title><description><![CDATA[Unter der Frage liegt noch eine Frage. An der Oberfläche klingt sie gewöhnlich. Mag sie mich? Habe ich etwas Falsches gesagt? Ist sie…]]></description><link>https://bdteo.com/de/kind-without-disappearing/</link><guid isPermaLink="false">https://bdteo.com/de/kind-without-disappearing/</guid><pubDate>Sun, 10 May 2026 12:14:00 GMT</pubDate><content:encoded>&lt;p&gt;Unter der Frage liegt noch eine Frage.&lt;/p&gt;
&lt;p&gt;An der Oberfläche klingt sie gewöhnlich. Mag sie mich? Habe ich etwas Falsches gesagt? Ist sie verärgert? Sollte ich mich besser erklären? Sollte ich lustiger sein, sanfter, ruhiger, nützlicher, weniger bedürftig, mehr Mann, weniger Problem?&lt;/p&gt;
&lt;p&gt;Aber unter all dem liegt eine schwerere Frage.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Bin ich ungefährlich, gut, genug und nicht wie die schlechten Männer?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Das ist die Frage, die ich immer wieder am Grund des Brunnens finde.&lt;/p&gt;
&lt;p&gt;Nicht, weil irgendjemand, der heute lebt, sie absichtlich dort abgelegt hat. Niemand hat mich hingesetzt und gesagt: du musst Liebe wie eine moralische Prüfung behandeln. Niemand hat diesen Satz in eine Wand geritzt. Es war leiser als das. Gewöhnlicher. Ein Haus hat Wetter, und Kinder lernen das Wetter, bevor sie Sprache lernen.&lt;/p&gt;
&lt;p&gt;Ich wurde hauptsächlich von Frauen aufgezogen, die mich liebten. Meine Mutter. Meine Großmutter. Für mich waren sie fast mythologisch, nicht weil sie makellos waren, sondern weil sie die Welt waren. Sie waren Zärtlichkeit, Essen, Schutz, Intelligenz, Opfer, Wärme. Sie waren auch verletzt.&lt;/p&gt;
&lt;p&gt;Mein Vater war abwesend, bis ich ihn viel später fand. Er war, vorsichtig gesagt, ein Mann vieler Frauen. Diese Abwesenheit hinterließ eine Form im Haus. Um diese Form herum wuchs eine Geschichte: Männer verletzen, Männer verraten, Männer nehmen, Männer gehen, Männer sind schmutzig, Männer sind schwach, Männer sind gefährlich.&lt;/p&gt;
&lt;p&gt;Dann kam die Ausnahme. Nicht du, mein Sohn. Du nicht.&lt;/p&gt;
&lt;p&gt;Aber ich glaube nicht, dass ein Kind die Ausnahme sauber hört. Ein Kind hört zuerst das Urteil und die Fußnote später. Männliche Natur ist gefährlich, und ich muss jeden Tag beweisen, dass meine es nicht ist.&lt;/p&gt;
&lt;p&gt;Also baute ich mir eine private Religion des Gutseins.&lt;/p&gt;
&lt;p&gt;Ein guter Mann öffnet die Tür. Ein guter Mann trägt die Tasche. Ein guter Mann lässt die Frau, die er liebt, nie allein leiden. Ein guter Mann nimmt die Last auf sich. Ein guter Mann bringt sie zum Lachen. Ein guter Mann heilt. Ein guter Mann gehorcht. Ein guter Mann erträgt Schmerz still. Ein guter Mann beschwert sich nicht. Ein guter Mann braucht nicht zu viel. Ein guter Mann ist nützlich genug, damit ihm vergeben wird, dass er existiert.&lt;/p&gt;
&lt;p&gt;Es klingt edel, bis man die Falle bemerkt.&lt;/p&gt;
&lt;p&gt;Wenn Gutsein endloser Dienst bedeutet, dann wird Liebe zu einer Schuld. Wenn Liebe eine Schuld ist, dann fühlt sich jedes &quot;Nein&quot; an wie eine Rechnung, die man nicht bezahlen kann. Wenn jede Grenze wie ein Beweis wirkt, dass man versagt hat, hört man nicht die Person vor sich. Man hört, wie das alte Gericht wieder zusammentritt.&lt;/p&gt;
&lt;p&gt;Dort habe ich Fehler gemacht.&lt;/p&gt;
&lt;p&gt;Nicht die lauten. Keine Vergeltung. Keine Grausamkeit. Mein Versagen ist leiser und demütigender.&lt;/p&gt;
&lt;p&gt;Ich breche zusammen.&lt;/p&gt;
&lt;p&gt;Ein kleines Nein landet im Raum. Kein Kino. Kein ständiges Schreiben. Nein, ich bin beschäftigt. Nein, ich bin müde. Nein, nicht jetzt.&lt;/p&gt;
&lt;p&gt;Das Ereignis an der Oberfläche ist klein. Die innere Explosion ist es nicht.&lt;/p&gt;
&lt;p&gt;Das Telefon wird zum Gerichtssaal. Eine unbeantwortete Nachricht wird zum Beweisstück. Eine Grenze wird zum Urteil. Ich fange an zu erklären, nicht weil ich etwas Neues zu sagen habe, sondern weil ich versuche, die Stille zu überleben.&lt;/p&gt;
&lt;p&gt;Das ist keine Wiedergutmachung.&lt;/p&gt;
&lt;p&gt;Das ist die Bitte an einen anderen Menschen, mich vor der Möglichkeit zu retten, dass ich schlecht bin.&lt;/p&gt;
&lt;p&gt;Eine Entschuldigung ist kein Zauberspruch. Sie ruft keine Vergebung herbei. Sie macht den anderen Menschen nicht verantwortlich dafür, zu beweisen, dass ich gut bin.&lt;/p&gt;
&lt;p&gt;Echte Wiedergutmachung ist weniger theatralisch. Ein Nein hören. Freundlich bleiben. Nicht jemand anderen für eine alte Wunde zahlen lassen.&lt;/p&gt;
&lt;p&gt;Das ist der Satz, den ich zu lernen versuche:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Ich kann freundlich sein, ohne zu verschwinden.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Freundlichkeit ist kein Gehorsam. Sanftheit ist keine Selbstauslöschung. Liebe ist keine Aufführung, in der ich 100% des Gewichts trage, bis mein Rücken bricht, und das Brechen dann Hingabe nenne.&lt;/p&gt;
&lt;p&gt;Ein guter Mann verschwindet nicht im Dienen.&lt;/p&gt;
&lt;p&gt;Ein guter Mann kann die Tür öffnen, weil er es möchte, nicht weil er Angst hat, eine Prüfung nicht zu bestehen. Er kann eine Tasche tragen, weil es liebevoll ist, nicht weil ein Kilogramm in der Hand eines anderen als Beweis gegen ihn zählt. Er kann ein Geschenk aus Freude kaufen, nicht aus Panik. Er kann jemanden zum Lachen bringen, ohne Lachen in einen Beweis seines Wertes zu verwandeln. Er kann schützen, ohne zu kontrollieren. Er kann sich entschuldigen, ohne sofortige Rettung vor Schuld zu verlangen.&lt;/p&gt;
&lt;p&gt;Und er kann ein Nein hören.&lt;/p&gt;
&lt;p&gt;Nicht perfekt. Nicht ohne Schmerz. Ich tue nicht so, als würde der Körper im Tempo der Sprache lernen. Manchmal trifft ein kleines Nein immer noch wie Donner. Manchmal machen Krankheit, Erschöpfung und Einsamkeit die alten Gedanken monströs. Aber ein Gefühl ist kein Gebot. Ein Nervensystem mit niedrigem Akku ist kein Orakel.&lt;/p&gt;
&lt;p&gt;Also brauche ich eine Übung, klein genug, um das echte Leben zu überstehen.&lt;/p&gt;
&lt;p&gt;Einmal atmen.&lt;/p&gt;
&lt;p&gt;Sagen: &quot;Ich verstehe. Kein Problem.&quot;&lt;/p&gt;
&lt;p&gt;Die Energie nach vorn bewegen.&lt;/p&gt;
&lt;p&gt;Nicht sofort erklären.&lt;/p&gt;
&lt;p&gt;Den anderen Menschen nicht bitten, den Zusammenbruch zu halten.&lt;/p&gt;
&lt;p&gt;Das Nein existieren lassen, ohne daraus das Ende der Verbindung zu machen.&lt;/p&gt;
&lt;p&gt;Das klingt fast dumm einfach. Ist es nicht. Es ist ein ganzes Leben Wetter, das gebeten wird, seine Richtung Atemzug für Atemzug zu ändern.&lt;/p&gt;
&lt;p&gt;Aber vielleicht ist genau das eigentlich Erlösung. Kein Berg. Kein leuchtender Wächter. Kein einzelner Satz, der mich rettet. Nur die wiederholte Weigerung, jemand anderen für eine alte Wunde zahlen zu lassen.&lt;/p&gt;
&lt;p&gt;Ich will keiner der schlechten Männer sein.&lt;/p&gt;
&lt;p&gt;Aber ich will auch nicht mein ganzes Leben darum bauen, zu beweisen, dass ich es nicht bin.&lt;/p&gt;
&lt;p&gt;Ich will etwas Reineres als das. Leiser. Menschlicher.&lt;/p&gt;
&lt;p&gt;Ich will weich sein und trotzdem ein Selbst haben.&lt;/p&gt;
&lt;p&gt;Ich will lieben, ohne mich selbst zur Bezahlung zu machen.&lt;/p&gt;
&lt;p&gt;Ich will freundlich sein, ohne zu verschwinden.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Der Pfeiler und der Efeu]]></title><description><![CDATA[Diskrete Mathematik ist voller kleiner Dinge, die offensichtlich wirken. Genau das ist die Falle. Du sitzt in der Vorlesung. Der Professor…]]></description><link>https://bdteo.com/de/the-pillar-and-the-ivy/</link><guid isPermaLink="false">https://bdteo.com/de/the-pillar-and-the-ivy/</guid><pubDate>Sun, 26 Apr 2026 12:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Diskrete Mathematik ist voller kleiner Dinge, die offensichtlich wirken. Genau das ist die Falle.&lt;/p&gt;
&lt;p&gt;Du sitzt in der Vorlesung. Der Professor zeichnet etwas an die Tafel. &lt;em&gt;Eine Invariante ist eine Eigenschaft P, die an jedem Kontrollpunkt einer Operation gilt.&lt;/em&gt; Du schreibst es auf, zuckst mit den Schultern, gehst einen Kaffee trinken. Und dann, zehn Jahre später, debuggst du um 2 Uhr morgens ein verteiltes System... und erst dann beginnt dieses Wort, dir etwas zu bedeuten.&lt;/p&gt;
&lt;p&gt;Das hier ist für die Version von dir, die noch in der Vorlesung sitzt.&lt;/p&gt;
&lt;h2&gt;Ein Pfeiler auf einem Feld&lt;/h2&gt;
&lt;p&gt;Stell dir einen alten Steinpfeiler vor, allein auf einem Feld. Nichts um ihn herum. Nichts, was ihm passiert.&lt;/p&gt;
&lt;p&gt;Das ist es, was dir die Lehrbuchdefinition gibt. Nur den Pfeiler.&lt;/p&gt;
&lt;h2&gt;Der Professor hat den Efeu vergessen&lt;/h2&gt;
&lt;p&gt;Mein Professor war übrigens großartig. Das Lehrbuch lügt nicht. Das Bild ist nur unvollständig.&lt;/p&gt;
&lt;p&gt;Lass jetzt also Efeu an dem Pfeiler wachsen. Ranken, die am Stein ziehen. Nistende Vögel. Ein Tourist mit einem Marker. Ein kleines Erdbeben. Ein Sturm. Zweihundert Jahre Wetter.&lt;/p&gt;
&lt;p&gt;Der Pfeiler steht immer noch da. Aus seiner Perspektive ist nichts passiert.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Das&lt;/em&gt; ist die Invariante.&lt;/p&gt;
&lt;p&gt;Lies jetzt die Lehrbuchzeile noch einmal — &lt;em&gt;eine Eigenschaft P, die an jedem Kontrollpunkt einer Operation gilt&lt;/em&gt;. Der Pfeiler ist die Eigenschaft. Der Efeu ist die Operation. Der Kontrollpunkt ist der Moment, in dem du vorbeigehst und hinschaust. &lt;em&gt;Gilt&lt;/em&gt; ist nur die lange Art zu sagen: &lt;em&gt;dem Pfeiler ist der Efeu egal&lt;/em&gt;.&lt;/p&gt;
&lt;h2&gt;Wo du ihr immer wieder begegnen wirst&lt;/h2&gt;
&lt;p&gt;Sobald du den Pfeiler hast, beginnst du, ihn überall zu sehen.&lt;/p&gt;
&lt;p&gt;Eine Schleifeninvariante. Dein Schleifenrumpf ist der Efeu. Deine Invariante ist der Pfeiler. Der Rumpf kann sie für einen Moment brechen, wie eine Ranke, die am Stein zieht. Bis zum nächsten Kontrollpunkt steht der Pfeiler wieder dort, wo er war.&lt;/p&gt;
&lt;p&gt;Eine Datenbanktransaktion. Zwischen BEGIN und COMMIT können die Daten Gymnastik machen. ROLLBACK ist der Gärtner, der kommt und den Efeu herunterreißt. Der Pfeiler — dein konsistenter Zustand — steht immer noch.&lt;/p&gt;
&lt;p&gt;ACID. Fremdschlüssel. Typsysteme. Verteilte Retries. Alles Pfeiler. Alle stehen in ihrem eigenen Efeu.&lt;/p&gt;
&lt;h2&gt;Ein Pfeiler, den du umarmen kannst&lt;/h2&gt;
&lt;p&gt;Ein kleiner Bonus, weil du noch liest.&lt;/p&gt;
&lt;p&gt;Es gibt ein Geschwisterkonzept namens &lt;strong&gt;Idempotenz&lt;/strong&gt;. Eine idempotente Operation ist etwas, das du viele Male tun kannst, und das Ergebnis ist dasselbe, als hättest du es einmal getan. ROLLBACK zehnmal aufzurufen ist dasselbe, wie es einmal aufzurufen. Einen Lichtschalter zehnmal auf &quot;ein&quot; zu setzen ist dasselbe, wie ihn einmal auf &quot;ein&quot; zu setzen.&lt;/p&gt;
&lt;p&gt;Wenn Invarianz &lt;em&gt;der Pfeiler ist, der sich nicht verändert, während der Efeu wild wuchert&lt;/em&gt;, dann ist Idempotenz &lt;em&gt;der Pfeiler, den du so oft umarmen kannst, wie du willst, und es macht ihm nichts aus&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Setz die beiden zusammen und du hast den Goldstandard für fehlertolerante Systeme. Netzwerk weg? Retry. Server abgestürzt? Retry. Du wirst in einem gültigen Zustand landen, und du kannst weiter retryen, ohne etwas kaputtzumachen.&lt;/p&gt;
&lt;p&gt;Ein Pfeiler, der den Efeu überlebt &lt;em&gt;und&lt;/em&gt; überlebt, tausendmal umarmt zu werden. Die meiste moderne Infrastruktur ist leise darauf gebaut.&lt;/p&gt;
&lt;h2&gt;Ein kleiner Schluss&lt;/h2&gt;
&lt;p&gt;Das ist das Bild, das ich mir gewünscht hätte, jemand hätte es mir vor zehn Jahren gezeichnet.&lt;/p&gt;
&lt;p&gt;Es ist nicht viel. Ein Bild. Aber manchmal ist ein einziges Bild der Unterschied zwischen einem Konzept, das dir in den Knochen sitzt, und einem Konzept, das in einer Fußnote lebt.&lt;/p&gt;
&lt;p&gt;Wenn du Student bist, Junior Engineer, oder einfach jemand, der bei dem Wort &quot;Invariante&quot; schon eine Weile still genickt hat... dann ist das hier für dich.&lt;/p&gt;
&lt;p&gt;Dem Pfeiler ist der Efeu egal. Das ist die ganze Sache.&lt;/p&gt;
&lt;p&gt;Von einem Underdog zum anderen.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Wo sich alle Wissenschaften treffen]]></title><description><![CDATA[Ich bin kein Mathematiker. Ich bin kein Philosoph. Ich bin kein Neurowissenschaftler. Ich bin einfach ein Mensch, der gern denkt... Und ich…]]></description><link>https://bdteo.com/de/where-all-sciences-meet/</link><guid isPermaLink="false">https://bdteo.com/de/where-all-sciences-meet/</guid><pubDate>Sat, 18 Apr 2026 12:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Ich bin kein Mathematiker. Ich bin kein Philosoph. Ich bin kein Neurowissenschaftler. Ich bin einfach ein Mensch, der gern denkt... Und ich erwische die Wissenschaften immer wieder dabei, wie sie einander zuzwinkern, wenn sie glauben, niemand schaut hin.&lt;/p&gt;
&lt;p&gt;Vielleicht lesen sie alle aus demselben Buch. Und die letzte Seite fehlt mit Absicht.&lt;/p&gt;
&lt;h2&gt;Sprache ist ein sehr abstrakter Code&lt;/h2&gt;
&lt;p&gt;Binär war einmal die niedrigste Sprache. Dann kam Assembly. Dann C. Dann tausend andere. Jede neue Sprosse ist nur eine kürzere Art, etwas Längeres zu sagen.&lt;/p&gt;
&lt;p&gt;Menschliche Sprache ist nur eine weitere Sprosse auf derselben Leiter. Wir kompilieren unsere Gedanken in Wörter. Andere Menschen dekompilieren diese Wörter in ihren eigenen Köpfen wieder zurück in Gedanken. Der Compiler ist verlustbehaftet. Immer. Das liegt in der Natur der Sache.&lt;/p&gt;
&lt;p&gt;Ein Satz ist ein High-Level-Programm. Eine Geschichte ist ein System. Ein Sprichwort ist eine winzige gecachte Funktion, die die Evolution vor sehr langer Zeit geschrieben hat.&lt;/p&gt;
&lt;h2&gt;Die Tür, die sich von innen nicht öffnen lässt&lt;/h2&gt;
&lt;p&gt;Gödel hat etwas bewiesen, das grausam und schön zugleich ist, weißt du. Jedes System, das groß genug ist, um interessant zu sein, kann seine eigene Vollständigkeit nicht von innen beweisen. Es gibt wahre Dinge über dich, die du mit deinen eigenen Werkzeugen nicht erreichen kannst.&lt;/p&gt;
&lt;p&gt;Tarski hat es noch schärfer gemacht. Das Wort &quot;wahr&quot; braucht in jeder Sprache eine größere Sprache, die es halten kann. Du kannst Wahrheit nicht von dort aus definieren, wo du stehst. Du musst nach draußen treten. Und in dem Moment, in dem du nach draußen trittst, bist du in einem neuen System, das dasselbe Problem hat.&lt;/p&gt;
&lt;p&gt;Es ist ein Flur voller Türen. Du öffnest eine. Dahinter ist eine weitere.&lt;/p&gt;
&lt;p&gt;Das ist Mathematik. Aber es ist auch Psychologie. Es ist auch Anthropologie. Es sind auch zwei Menschen, die versuchen, sich darauf zu einigen, was ein einziges Wort bedeutet, und es nie ganz schaffen.&lt;/p&gt;
&lt;p&gt;Niemand ist ein Orakel. Nicht ich. Nicht du. Nicht der klügste Mensch, dem du je begegnet bist. Das ist nichts Trauriges. Das ist die Tür.&lt;/p&gt;
&lt;h2&gt;Gegenseitige Ausrichtung&lt;/h2&gt;
&lt;p&gt;Wenn zwei Menschen sprechen, ist keiner von beiden ein Orakel. Ausrichtung heißt nicht, dass ich dich korrigiere oder du mich. Es heißt, dass wir beide Fragen stellen, um die unsichtbare Lücke zwischen dem zu finden, was gesagt wurde, und dem, was gemeint war.&lt;/p&gt;
&lt;p&gt;Wahrheit gehört niemandem. Wahrheit wird trianguliert.&lt;/p&gt;
&lt;p&gt;Du bist meine Metasprache. Ich bin deine. Wir prüfen einander auf Unvollständigkeit, die wir allein nicht sehen können.&lt;/p&gt;
&lt;h2&gt;Das Gehirn ist nicht faul&lt;/h2&gt;
&lt;p&gt;Unser Gehirn tut mit der Energie, die es bekommen hat, sein Bestes. Es ist nicht faul. Es ist ein Optimierer. Die Evolution hat es nicht dafür bezahlt, recht zu haben. Die Evolution hat es dafür bezahlt, mit einem Beutel Nüsse und einem kleinen See zu überleben.&lt;/p&gt;
&lt;p&gt;Der Trick ist nicht, gegen das Gehirn zu kämpfen. Der Trick ist, mit ihm zusammenzuarbeiten.&lt;/p&gt;
&lt;p&gt;Eine Frage lässt es aufleuchten. Eine Frage ist ein winziges Gratis-Mittagessen. Eine Frage ist die Art, wie man einen müden Geist weckt, ohne zu schreien.&lt;/p&gt;
&lt;p&gt;Deshalb ist Zusammenfassen auch die schwierigste Kunst. Zusammenfassen heißt, dem Material eine einzige Frage zu stellen und alles wegzuwerfen, was keine Antwort ist.&lt;/p&gt;
&lt;h2&gt;Bulgarien spricht&lt;/h2&gt;
&lt;p&gt;Wir haben ein Sprichwort. &lt;strong&gt;Седем пъти мери, един път режи.&lt;/strong&gt; &lt;em&gt;Siebenmal messen, einmal schneiden.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Es geht nicht darum, langsam zu sein. Es geht darum zu wissen, dass dir eine Stunde Denken später ein Jahr falschen Code erspart, oder falsche Liebe, oder die falsche Laufbahn. Denken ist billig. Schneiden ist teuer.&lt;/p&gt;
&lt;p&gt;Wir haben noch eines. &lt;strong&gt;Рибата винаги започва да мирише от главата.&lt;/strong&gt; &lt;em&gt;Der Fisch fängt immer am Kopf an zu stinken.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Wenn die Spitze verfault ist, ist alles darunter auch schon verfault. Es weiß es nur noch nicht.&lt;/p&gt;
&lt;p&gt;Beide Sprichwörter sagen, was Gödel gesagt hat, nur in bäuerlicher Kleidung. Du kannst dich nicht von innen beweisen. Prüfe dich an etwas anderem. An jemand anderem. An der Wirklichkeit.&lt;/p&gt;
&lt;h2&gt;Engineering, aber ehrlich&lt;/h2&gt;
&lt;p&gt;Engineering wird, wenn man es ehrlich betreibt, leise zu Philosophie.&lt;/p&gt;
&lt;p&gt;Du baust immer gegen eine Spezifikation, die niemals vollständig sein kann. Gödel hat dir das versprochen. Also lernst du, für das Unbekannte zu entwerfen, statt so zu tun, als wäre es nicht da. Du stellst noch eine Frage. Du baust etwas, das korrigiert werden kann, wenn die Wirklichkeit eintrifft.&lt;/p&gt;
&lt;p&gt;Ich meine Engineering nicht im engen Sinn. Ich meine das Handwerk, irgendetwas zu bauen, das in der Wirklichkeit bestehen muss. Ein Produkt. Eine Beziehung. Ein Leben. Das Handwerk ist immer dasselbe. Ehrlicher Ehrgeiz trifft auf ehrliche Grenzen.&lt;/p&gt;
&lt;p&gt;Das ist Weisheit. Nicht die Weisheit aus Büchern. Die Weisheit, einem Problem in die Augen zu schauen und zu sagen: &lt;em&gt;Ich kann dich nicht ganz kennen. Aber ich werde etwas bauen, das dich trotzdem halten kann.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Das ist ungefähr so nah, wie Engineering der Weisheit kommt.&lt;/p&gt;
&lt;h2&gt;Exzellenz ist das Gehen&lt;/h2&gt;
&lt;p&gt;Lyndon B. Johnson sagte: &lt;em&gt;&quot;Die edelste Suche von heute ist die Suche nach Exzellenz.&quot;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Früher dachte ich, Exzellenz sei eine Ziellinie. Ist sie nicht. Exzellenz ist das Gehen. Das siebenmalige Messen. Die eine weitere Frage. Respekt vor dem, was du machst, und auch Respekt vor dem, was aus dir wird, während du es machst.&lt;/p&gt;
&lt;p&gt;Exzellenz ist kein Ort, an dem du ankommst. Exzellenz ist einfach das, was geschieht, wenn du weiterfragst.&lt;/p&gt;
&lt;h2&gt;Wenn du es einem Kind nicht erklären kannst&lt;/h2&gt;
&lt;p&gt;Wenn du es einem Kind nicht erklären kannst, verstehst du es selbst nicht.&lt;/p&gt;
&lt;p&gt;An den meisten Tagen falle ich bei diesem Test durch. Aber das Scheitern ist nützlich. Wenn meine Erklärung lang ist, habe ich nicht verstanden. Wenn ich Fachjargon brauche, habe ich nicht verstanden. Wenn das Kind immer noch verwirrt weggeht, bin ich das Problem. Nicht das Kind.&lt;/p&gt;
&lt;p&gt;Weißt du, ein Kind weiß noch nicht, was man angeblich nicht fragen soll. Ein Kind stellt die Frage, von der du gehofft hattest, dass sie niemand stellt. Deshalb sind Kinder der Wahrheit näher als die meisten von uns.&lt;/p&gt;
&lt;h2&gt;Wo sich alle Wissenschaften treffen&lt;/h2&gt;
&lt;p&gt;Die Mathematik gibt Unvollständigkeit zu. Die Linguistik gibt die Lücke zwischen Zeichen und Ding zu. Die Neurowissenschaft gibt zu, dass das Gehirn sich selbst nicht vollständig beobachten kann. Die Philosophie sagt das seit Jahrhunderten, unmodern und geduldig. Die Anthropologie beobachtet, wie Menschen denselben Kreis auf tausend verschiedene Lehmböden zeichnen. Die Evolution flüstert: Dein bestes Denken ist immer noch das Denken eines Affen, nur besser angezogen.&lt;/p&gt;
&lt;p&gt;Sie alle kommen in ihrer Tiefe am selben Ort an. Sie stehen vor einer Tür, die sie von innen nicht öffnen können.&lt;/p&gt;
&lt;p&gt;Ich sage jetzt eine Sache, und du kannst damit machen, was du willst.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Wo sich alle Wissenschaften treffen, dort wohnt Gott.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Nicht der Gott eines bestimmten Buches. Nicht ein Gott mit einem bestimmten Namen. Ich meine nur das hier. Am Rand jeder ehrlichen Untersuchung gibt es eine Stille, die nicht leer ist. Es gibt ein Unerkennbares, das uns irgendwie trotzdem lehrt.&lt;/p&gt;
&lt;p&gt;Nenn es Gott. Nenn es das Geheimnis. Nenn es, wie auch immer deine Sprache es sich leisten kann. Es ist immer da. Es läuft nie weg. Und es ist immer auf der anderen Seite der Tür, die du von innen nicht öffnen kannst.&lt;/p&gt;
&lt;h2&gt;Das kleine Ende&lt;/h2&gt;
&lt;p&gt;Deshalb denke ich so viel. Deshalb stelle ich Fragen, die ich nicht beantworten kann. Deshalb baue ich immer weiter Dinge, selbst wenn ich weiß, dass die Dinge niemals vollständig sein werden.&lt;/p&gt;
&lt;p&gt;Denn Unvollständigkeit ist kein Bug.&lt;/p&gt;
&lt;p&gt;Sie ist die Tür.&lt;/p&gt;
&lt;p&gt;Und auf der anderen Seite jeder Tür wartet etwas darauf, dass jemand anklopft.&lt;/p&gt;
&lt;p&gt;Ich lerne noch, anzuklopfen.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Das Modell, das nicht da war]]></title><description><![CDATA[Wir generierten Anzeigenbilder mit Gemini 3 Pro. Platz #4 im Artificial-Analysis-Leaderboard. Die Qualität war wirklich beeindruckend…]]></description><link>https://bdteo.com/de/the-model-that-wasnt-there/</link><guid isPermaLink="false">https://bdteo.com/de/the-model-that-wasnt-there/</guid><pubDate>Sat, 14 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Wir generierten Anzeigenbilder mit Gemini 3 Pro. Platz #4 im Artificial-Analysis-Leaderboard. Die Qualität war wirklich beeindruckend - bessere Prompt-Treue, bessere Typografie, besserer kreativer Output als alles andere, was wir ausprobiert hatten. Google war damit überall. YouTube-Videos. Konferenzen. Seminare. Blogposts. &quot;Das beste Modell zur Bildgenerierung der Welt.&quot;&lt;/p&gt;
&lt;p&gt;Ich glaubte ihnen. Die Bilder waren gut.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Dann meldete ein Nutzer, dass das Klonen einer Anzeige vier Minuten dauerte. Ich sah nach. Die Generierung selbst war in unter dreißig Sekunden fertig. Die anderen dreieinhalb Minuten? Der Job versuchte es immer wieder gegen eine Wand.&lt;/p&gt;
&lt;ol start=&quot;429&quot;&gt;
&lt;li&gt;Resource Exhausted.&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;p&gt;Google hatte für die Gemini-Bildgenerierung ein hartes Limit gesetzt: zwei Anfragen pro Minute. Pro Projekt. Global.&lt;/p&gt;
&lt;p&gt;Zwei. Nicht zweihundert. Nicht zwanzig. Zwei.&lt;/p&gt;
&lt;p&gt;Am Tag davor hatten wir 900 Bilder ohne Problem generiert. Auf ihrer Seite hatte sich etwas geändert. Keine Mitteilung, keine E-Mail, kein Changelog-Eintrag. Nur eine neue Decke, niedrig genug, dass zwei Nutzer, die gleichzeitig klicken, sie erreichen.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Unser DevOps reichte einen Antrag auf Quota-Erhöhung ein. Dreißig RPM. Vernünftig für ein Production-SaaS. Die Antwort von Google:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&quot;This gemini model is not available for quota increase.&quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Sie schlugen vor, auf Imagen 4 zu wechseln. Ich sah es nach.&lt;/p&gt;
&lt;p&gt;Imagen 4 Ultra - Platz #10. Imagen 4 Standard - #42. Imagen 4 Fast - #60.&lt;/p&gt;
&lt;p&gt;Wir waren auf #4. Googles Vorschlag war ein Downgrade um irgendwo zwischen sechs und sechsundfünfzig Plätze auf ihrem eigenen Leaderboard.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Ich probierte alles aus, was mir einfiel.&lt;/p&gt;
&lt;p&gt;Wechsel zu Gemini 3.1 Flash - Platz #2, halb so teuer, besser als das, was wir hatten. Auf Staging deployed. Dann prüfte ich die Quota. Dasselbe Limit von 2 RPM. Es ist nicht pro Modell. Es ist pro Projekt, pro Base-Model-Family. Jedes Gemini-Bildmodell teilt sich denselben Bucket.&lt;/p&gt;
&lt;p&gt;Multi-Region-Verteilung - die Quota gilt pro Region, also würden Anfragen über fünf Regionen verteilt zehn RPM ergeben. Nur funktionieren Gemini-3.x-Bildmodelle ausschließlich über den globalen Endpoint. Es gibt keine regionalen Endpoints. Die 2 RPM auf dem globalen Endpoint sind der einzige Bucket, der existiert.&lt;/p&gt;
&lt;p&gt;Mehrere GCP-Projekte - jedes bekommt seine eigenen 2 RPM. Technisch funktioniert das. Architektonisch sieht so Verzweiflung aus.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Ich begann zu recherchieren, was andere Entwickler erlebten. Überall dieselbe Geschichte. Undokumentiertes 2-RPM-Limit. Forenposts ohne Antwort von Google. Genehmigte Quota-Erhöhungen, die trotzdem bei jedem Aufruf 429 zurückgaben. Unsere monatlichen $30K GCP-Ausgaben? Helfen nicht. Die Standard-PayGo-Tiers schließen Bildgenerierungsmodelle ausdrücklich von Throughput-Vorteilen aus.&lt;/p&gt;
&lt;p&gt;Google wird dieses Limit nicht erhöhen.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Und dann die interessante Frage: Warum nicht?&lt;/p&gt;
&lt;p&gt;Gemini generiert Bilder mit demselben autoregressiven Transformer, der Text verarbeitet. Es ist kein Diffusionsmodell. Es ist das volle LLM, das sich Pixel für Pixel durch ein Bild hindurchdenkt. Jedes Bild verbrennt so viel Compute wie Dutzende Text-API-Aufrufe.&lt;/p&gt;
&lt;p&gt;Bei $0.067 pro Bild verliert Google mit ziemlicher Sicherheit Geld bei jeder Generierung. Das 2-RPM-Limit ist keine Quota, die sie vergessen haben anzupassen. Es ist eine berechnete Drosselung, weil die Ökonomie nicht funktioniert.&lt;/p&gt;
&lt;p&gt;Imagen 4 nutzt klassische latente Diffusion - um Größenordnungen billiger im Betrieb. Deshalb bekommt es 30-150 RPM, und deshalb drängt Google alle dorthin. Das teure Modell bekommt das Marketing. Das billige Modell bekommt den Durchsatz.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Denk darüber nach, was das bedeutet. Google hat ein Modell gebaut, das jeden Benchmark anführte. Sie haben es auf jeder Konferenz vermarktet, in jeder YouTube-Keynote, in jedem Entwicklerblog. &quot;State of the art. Das beste der Welt.&quot; Entwickler integrieren es in Production. Nutzer verlassen sich darauf. Dann: zwei Anfragen pro Minute, keine Erhöhung verfügbar, verwendet stattdessen unser schlechteres Modell.&lt;/p&gt;
&lt;p&gt;Die API existiert. Der Endpoint funktioniert. Die Demo ist atemberaubend.&lt;/p&gt;
&lt;p&gt;Aber du kannst es nicht wirklich benutzen.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Wir wechselten zu &lt;code class=&quot;language-text&quot;&gt;gemini-2.5-flash-image&lt;/code&gt;. Dem älteren Modell. Dem langweiligen. Dem, über das niemand YouTube-Videos macht.&lt;/p&gt;
&lt;p&gt;Es hat 40 RPM. Es funktioniert.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Vier Lektionen, verdichtet:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Marketing ist kein Produkt.&lt;/strong&gt; Ein Leaderboard anzuführen heißt nicht, dass du Production-Traffic bedienen kannst. Benchmarks messen Qualität. Rate Limits messen Commitment.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Autoregressive Bildgenerierung skaliert nicht.&lt;/strong&gt; Wenn ein Bild so viel kostet wie hundert Textabfragen, überlebt kein Geschäftsmodell großzügige Rate Limits. Die Ökonomie ist das Signal.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Preview bedeutet Preview.&lt;/strong&gt; Google kann Limits ändern, Modelle abschalten oder dich ohne Vorwarnung zu schlechteren Alternativen umleiten. Wenn dein Production-System von einem Preview-Modell abhängt, hängt dein Production-System am Marketingkalender eines anderen.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Das langweilige Modell funktioniert.&lt;/strong&gt; Das mit 40 RPM und ohne Konferenzvorträge wird deine Nutzer bedienen, während das Weltklasse-Modell hinter einer Samtkordel sitzt und zwei Bilder pro Minute generiert.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Der unheimlichste Vendor-Lock-in ist der, der mit einer Demo beginnt, der du nicht widerstehen kannst.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Die Stadt, die nicht da war]]></title><description><![CDATA[Ich baute ein Ding, das Daten aus einer Quelle zieht, sie säubert und besser darstellt als das Original. Standardarbeit. Dann fragte ich den…]]></description><link>https://bdteo.com/de/the-city-that-wasnt-there/</link><guid isPermaLink="false">https://bdteo.com/de/the-city-that-wasnt-there/</guid><pubDate>Sun, 08 Feb 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Ich baute ein Ding, das Daten aus einer Quelle zieht, sie säubert und besser darstellt als das Original. Standardarbeit.&lt;/p&gt;
&lt;p&gt;Dann fragte ich den zweitgrößten Eintrag im System ab. Alles andere lieferte Hunderte Ergebnisse. Dieser eine: null. Nicht kaputt. Einfach leer.&lt;/p&gt;
&lt;p&gt;Ich nahm an, ich hätte es verbockt. Prüfte meinen Code dreimal. Testete den Endpoint direkt. Der Eintrag existiert in ihrer Oberfläche. Er ist nur ... hohl.&lt;/p&gt;
&lt;p&gt;Da fing ich an zu graben.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Die Quelle wirkte umfassend. Breiter Umfang, polierte Oberfläche, saubere API. Aber freiwillige Teilnahme bedeutet Lücken, die man in der Dokumentation nicht sieht.&lt;/p&gt;
&lt;p&gt;Drei Wettbewerber hatten Daten für dieselben Entitäten. Vollständige Datensätze. Die Information existiert also irgendwo.&lt;/p&gt;
&lt;p&gt;Dir fehlte kein Endpoint. Dem Endpoint fehlte die Wirklichkeit.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Ich stellte eine Frage, die niemand zu stellen schien: Wo lebten diese Daten vor dem Internet?&lt;/p&gt;
&lt;p&gt;Die Antwort: gedruckte Periodika. Archive. Analoge Formate, die seit dem 19. Jahrhundert laufen. Dreimal pro Woche veröffentlicht. Keine strukturierten Daten, nur Dokumente auf einer Website.&lt;/p&gt;
&lt;p&gt;Also lade ich eins herunter. Dichte institutionelle Prosa, Bekanntmachungen vergraben über mehrere Unterstellen hinweg. Die Daten sind da.&lt;/p&gt;
&lt;p&gt;Meine Wettbewerber machen das seit dreißig Jahren manuell.&lt;/p&gt;
&lt;p&gt;Ich schreibe an einem Nachmittag einen Scraper. Teils Neugier, teils Trotz.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Beim Dokumentenparsing wird es richtig schmerzhaft.&lt;/p&gt;
&lt;p&gt;Ein einzelnes Wort wird durch ein weiches Trennzeichen gespalten - Unicode U+00AD, für das Auge unsichtbar, für jede Regex fatal. Du starrst auf den Bildschirm und denkst, dein Pattern sei falsch. Ist es nicht. Da versteckt sich ein Geisterzeichen im Text. JavaScripts &lt;code class=&quot;language-text&quot;&gt;\w&lt;/code&gt; matcht keine Nicht-ASCII-Zeichen, also werden gewöhnliche Wörter zu unmöglichen Treffern. Zahlen enthalten Phantom-Leerzeichen aus dem Renderer: &quot;20. 000&quot; statt &quot;20.000.&quot;&lt;/p&gt;
&lt;p&gt;Jeder Bug braucht länger zum Finden als zum Fixen. Das ist bei Textextraktion immer das Verhältnis: 90% Detektivarbeit, 10% Code.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Zehn Datensätze materialisieren sich aus dem Rauschen. Daten, Kennungen, Orte - alles dort, wo es sein soll. Ich lasse es zweimal laufen, um sicherzugehen, dass ich nicht halluziniere. Gleiches Ergebnis. Es funktioniert wirklich.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Parsing zeigt dir, was da ist. Ich beginne zu suchen, was nicht da ist. IDs sind fortlaufend. Ich zähle sie durch.&lt;/p&gt;
&lt;p&gt;53% sind tot. Das System löscht abgeschlossene Einträge - kein Archiv, keine Historie. Manche Datensätze existieren, haben aber null begleitende Dokumente. Die Antwort: Besuchen Sie uns persönlich. Im Jahr 2026.&lt;/p&gt;
&lt;p&gt;Die Quelle ist keine Datenbank. Sie ist ein Fenster - und jemand schließt es ständig.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Die erste Datenquelle formte die Architektur. Die zweite zerlegte jede Annahme.&lt;/p&gt;
&lt;p&gt;Ich brauchte eine zweite Architektur. Das ist die höfliche Art zu sagen, dass die erste eigentlich gar keine Architektur war - nur eine funktionierende Lösung, die zufällig auf einen Fall passte. Die seltsame Quelle zeigt die Wahrheit: Du hast für die Daten gebaut, die du hattest, nicht für die Daten, denen du begegnen wirst.&lt;/p&gt;
&lt;p&gt;Diesmal baue ich eine richtige. Registry Pattern, gemeinsame Interfaces, Basiskontrakte, die jede Implementierung sich selbst treu bleiben lassen.&lt;/p&gt;
&lt;p&gt;Die Architektur ist besser, weil ich gewartet habe. Hätte ich sie am ersten Tag gebaut, hätte ich für die einzige Quelle entworfen, die ich kannte. Die zweite - die seltsame - zwang mich herauszufinden, worauf es wirklich ankommt.&lt;/p&gt;
&lt;p&gt;Du kannst nicht für das Unbekannte entwerfen. Aber du kannst refactoren, wenn es ankommt.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Die Architektur lehrte mich, wie ich bauen soll. Der Markt lehrte mich, wofür.&lt;/p&gt;
&lt;p&gt;Ich betrete einen Markt mit einem Platzhirsch, der seit dreißig Jahren läuft. Seine Technik sieht aus wie 2005. Sein Burggraben ist nicht Technologie - es sind Vertrauen, Markenbekanntheit, Jahrzehnte angesammelter Daten.&lt;/p&gt;
&lt;p&gt;Der moderne Wettbewerber startete vor drei Jahren mit KI und glatter UI. Unterbot den Platzhirsch. Drei Jahre später dominiert der Platzhirsch immer noch. Wie sich herausstellt, bedeutet billiger nicht automatisch besser positioniert.&lt;/p&gt;
&lt;p&gt;Anker zählen: Der erste Preis wird zum Referenzpunkt. Später leicht zu senken, nahezu unmöglich zu erhöhen. Das Abo ist nicht das Produkt - es ist das Tor zu dem, was dahinter liegt.&lt;/p&gt;
&lt;p&gt;Ich setze den Preis hoch an. Runter kann ich immer.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Vier Lektionen, kondensiert:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Autoritativ heißt nicht vollständig.&lt;/strong&gt; Der Primärquelle fehlte ein ganzes Segment. Die Daten existierten - nur nicht dort, wo sie irgendjemand erwartete.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Die zweite Quelle offenbart deine Architektur.&lt;/strong&gt; Du lernst die Wahrheit ueber dein Design erst, wenn etwas die Form verweigert, die du gebaut hast.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Daten sind nicht dauerhaft.&lt;/strong&gt; Wenn du sie brauchst, speichere sie. Die Quelle wird es nicht tun.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Setze den Preis für das, was du wirst, nicht für das, was du bist.&lt;/strong&gt; Das Abo ist eine Tür. Baue, was dahinter liegt.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Die interessante Arbeit lebt in den Lücken. Ich auch.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Die Queue, die nie aufhörte]]></title><description><![CDATA[E-Mails schlugen fehl. Dieser Teil war erwartet — kaputte SMTP-Zugangsdaten während einer Migration. Unerwartet war etwas anderes: Sie…]]></description><link>https://bdteo.com/de/the-queue-that-never-stopped/</link><guid isPermaLink="false">https://bdteo.com/de/the-queue-that-never-stopped/</guid><pubDate>Sat, 07 Feb 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;E-Mails schlugen fehl. Dieser Teil war erwartet — kaputte SMTP-Zugangsdaten während einer Migration. Unerwartet war etwas anderes: Sie hörten nie auf fehlzuschlagen.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Horizon-Dashboard: grün. Worker: gesund. Redis: wuchs langsam. Keine Alerts, keine Fehler in den Logs. Nur eine stille Ansammlung von Jobs, die es weiter und weiter und weiter versuchten.&lt;/p&gt;
&lt;p&gt;Ich bemerkte es nur, weil der Redis-Speicher nach dem Fix der SMTP-Konfiguration nicht wieder herunterkam. Irgendetwas war noch darin und kaute sich durch Retries. Tausende davon.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Ich nahm an, die Queue würde damit umgehen. Das ist der Deal: Ein Job schlägt fehl, versucht es ein paarmal erneut, landet in &lt;code class=&quot;language-text&quot;&gt;failed_jobs&lt;/code&gt;. Man macht weiter.&lt;/p&gt;
&lt;p&gt;Außer der Job ist ein Mailable.&lt;/p&gt;
&lt;p&gt;Wenn man ein Mailable in eine Queue dispatcht, wickelt Laravel es in einen Job ein. Das &lt;code class=&quot;language-text&quot;&gt;maxTries&lt;/code&gt; dieses Jobs kommt aus der &lt;code class=&quot;language-text&quot;&gt;$tries&lt;/code&gt;-Property des Mailables. Wenn man sie nicht setzt — und warum sollte man, die Doku erwähnt sie kaum — wird sie als &lt;code class=&quot;language-text&quot;&gt;null&lt;/code&gt; serialisiert.&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;null&lt;/code&gt; bedeutet nicht: &quot;nimm den Supervisor-Default.&quot; &lt;code class=&quot;language-text&quot;&gt;null&lt;/code&gt; bedeutet: &quot;kein Limit.&quot; Horizon sieht &lt;code class=&quot;language-text&quot;&gt;null&lt;/code&gt; und denkt: Dieser Job will für immer retryen. Also tut er es.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Wie sich herausstellt, ist es ein bekannter Bug. &lt;a href=&quot;https://github.com/laravel/horizon/issues/1346&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Laravel Horizon issue #1346&lt;/a&gt;. Das &lt;code class=&quot;language-text&quot;&gt;--tries&lt;/code&gt;-Flag des Supervisors wird ignoriert, wenn der serialisierte Job-Payload &lt;code class=&quot;language-text&quot;&gt;maxTries: null&lt;/code&gt; trägt. Die eigene Deklaration des Jobs gewinnt, und seine Deklaration sagt: niemals aufhören.&lt;/p&gt;
&lt;p&gt;Neunundzwanzig Mailable-Klassen. Jede einzelne ohne explizite &lt;code class=&quot;language-text&quot;&gt;$tries&lt;/code&gt;-Property. Jede einzelne potenziell unsterblich.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Der Fix ist in seiner Einfachheit fast beleidigend:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;php&quot;&gt;&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-php&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name-definition class-name&quot;&gt;WelcomeEmail&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Mailable&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;implements&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ShouldQueue&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword type-declaration&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$tries&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword type-declaration&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$maxExceptions&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Zwei Properties. Neunundzwanzig Dateien. Das ist alles.&lt;/p&gt;
&lt;p&gt;Ein erster Versuch, ein Retry, dann &lt;code class=&quot;language-text&quot;&gt;failed_jobs&lt;/code&gt;. So, wie ich dachte, dass es immer funktioniert.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Ich teste es so, wie man eine Mausefalle testen würde. Die SMTP-Konfiguration absichtlich kaputt machen. Eine E-Mail dispatchen. Horizon beobachten. Zwei Versuche. Failed job. Fertig. Keine Geister in der Queue.&lt;/p&gt;
&lt;p&gt;Dann fixe ich die anderen achtundzwanzig.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Drei Lektionen, verdichtet:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;&lt;code class=&quot;language-text&quot;&gt;null&lt;/code&gt; ist nicht &quot;default&quot;.&lt;/strong&gt; In serialisierten Job-Payloads bedeutet &lt;code class=&quot;language-text&quot;&gt;maxTries: null&lt;/code&gt; unbegrenzt. Deine Supervisor-Konfiguration ist ein Vorschlag, keine Regel.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Grüne Dashboards lügen.&lt;/strong&gt; Horizon zeigte gesunde Worker, die fröhlich Jobs verarbeiteten, die niemals fertig werden würden.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Framework-Defaults sind nicht immer vernünftig.&lt;/strong&gt; Laravel setzt &lt;code class=&quot;language-text&quot;&gt;$tries&lt;/code&gt; bei Mailables nicht. Du musst das tun. Die Doku wird dich nicht warnen, bis du schon ein Feuer hast.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Die unheimlichsten Bugs sind die, die wie normaler Betrieb aussehen. Dieser hier tat das — wochenlang.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Die nukleare Option für Bulk-Löschungen: TRUNCATE + Wiedereinfügen (MySQL/InnoDB)]]></title><description><![CDATA[Du musst Millionen von Zeilen aus einer MySQL-Tabelle löschen. Du greifst zu: Und dann siehst du zu, wie der Fortschrittsbalken in Echtzeit…]]></description><link>https://bdteo.com/de/todo-bulk-deletion-nuclear-option/</link><guid isPermaLink="false">https://bdteo.com/de/todo-bulk-deletion-nuclear-option/</guid><pubDate>Sat, 13 Dec 2025 13:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Du musst Millionen von Zeilen aus einer MySQL-Tabelle löschen.&lt;/p&gt;
&lt;p&gt;Du greifst zu:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sql&quot;&gt;&lt;pre class=&quot;language-sql&quot;&gt;&lt;code class=&quot;language-sql&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;DELETE&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;FROM&lt;/span&gt; big_table &lt;span class=&quot;token keyword&quot;&gt;WHERE&lt;/span&gt; some_condition&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Und dann siehst du zu, wie der Fortschrittsbalken in Echtzeit altert.&lt;/p&gt;
&lt;p&gt;Du versuchst, verantwortungsvoll zu sein, und machst es in Chunks:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sql&quot;&gt;&lt;pre class=&quot;language-sql&quot;&gt;&lt;code class=&quot;language-sql&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;DELETE&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;FROM&lt;/span&gt; big_table &lt;span class=&quot;token keyword&quot;&gt;WHERE&lt;/span&gt; some_condition &lt;span class=&quot;token keyword&quot;&gt;LIMIT&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;10000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;-- repeat until done&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Besser. Immer noch langsam. Immer noch laut. Immer noch teuer.&lt;/p&gt;
&lt;p&gt;Wenn du den &lt;strong&gt;Großteil&lt;/strong&gt; der Tabelle löschst (Faustregel: &lt;strong&gt;~80%+&lt;/strong&gt;), gibt es einen anderen Zug, der brutal effektiv ist:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Lösche nicht, was du nicht willst. &lt;strong&gt;Behalte, was du willst, und spreng den Rest weg.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Ich nenne es die &lt;strong&gt;nukleare Option&lt;/strong&gt;: &lt;strong&gt;TRUNCATE + Wiedereinfügen&lt;/strong&gt;.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;Warum &lt;code class=&quot;language-text&quot;&gt;DELETE&lt;/code&gt; langsam bleibt (auch in Chunks)&lt;/h2&gt;
&lt;p&gt;InnoDB &quot;entfernt&quot; Zeilen nicht einfach. Es arbeitet.&lt;/p&gt;
&lt;p&gt;Viel Arbeit:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Operationen Zeile für Zeile&lt;/strong&gt;: finden, sperren, als gelöscht markieren.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Indexpflege&lt;/strong&gt;: jede Löschung berührt jeden sekundären Index.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Undo-/Redo-Logging&lt;/strong&gt;: die Engine muss Rollback und Recovery weiterhin ermöglichen.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Buffer-Pool-Churn&lt;/strong&gt;: du machst ständig Pages dirty und verdrängst nützliche.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Replikationsauswirkung&lt;/strong&gt;: große Delete-Streams sind eine hervorragende Methode, Replica-Lag zu erzeugen.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Realitätscheck auf der Rückseite eines Umschlags:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;27 Mio. Zeilen bei ~6.000 Zeilen/Sek. ≈ &lt;strong&gt;75 Minuten&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Das ist kein Bug. Das ist das Kostenmodell, das du gewählt hast.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;Die nukleare Option: TRUNCATE + Wiedereinfügen&lt;/h2&gt;
&lt;p&gt;Diese Technik dreht das Kostenmodell um.&lt;/p&gt;
&lt;p&gt;Statt pro gelöschter Zeile zu zahlen, zahlst du pro &lt;strong&gt;behaltener&lt;/strong&gt; Zeile.&lt;/p&gt;
&lt;p&gt;Algorithmus:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;1) Copy the rows you want to keep into a temporary table
2) TRUNCATE the original table (fast)
3) Insert the preserved rows back into the original table
4) Drop the temp table&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Und ja: Sie heißt aus gutem Grund &quot;nuklear&quot;. Sie ist absichtlich stumpf.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;Warum es schnell ist&lt;/h2&gt;
&lt;p&gt;Die Gewinne sind mechanisch:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Operation&lt;/th&gt;
&lt;th align=&quot;right&quot;&gt;Grobe Kosten&lt;/th&gt;
&lt;th&gt;Warum&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;TRUNCATE&lt;/code&gt;&lt;/td&gt;
&lt;td align=&quot;right&quot;&gt;~O(1)&lt;/td&gt;
&lt;td&gt;löscht und erstellt die Tabelle neu (auf Metadatenebene)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;CREATE TABLE … AS SELECT&lt;/code&gt;&lt;/td&gt;
&lt;td align=&quot;right&quot;&gt;O(k)&lt;/td&gt;
&lt;td&gt;sequentieller Scan + Bulk-Write für behaltene Zeilen&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;INSERT … SELECT&lt;/code&gt;&lt;/td&gt;
&lt;td align=&quot;right&quot;&gt;O(k)&lt;/td&gt;
&lt;td&gt;Bulk-Insert; keine &quot;Delete-Steuer&quot;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Kein Delete-Overhead pro Zeile. Keine Indexupdates für die entfernten Zeilen (weil sie in einem Zug weg sind).&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;Wann du es verwenden solltest (und wann nicht)&lt;/h2&gt;
&lt;h3&gt;Verwende es, wenn&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Du den &lt;strong&gt;Großteil&lt;/strong&gt; der Tabelle löschst (noch einmal: &lt;strong&gt;~80%+&lt;/strong&gt; ist die Linie, ab der das hier anfängt zu glänzen).&lt;/li&gt;
&lt;li&gt;Du die &quot;Zeilen, die bleiben sollen&quot; sauber definieren kannst.&lt;/li&gt;
&lt;li&gt;Du kurze Nichtverfügbarkeit / ein Wartungsfenster verkraften kannst.&lt;/li&gt;
&lt;li&gt;Die Tabelle nicht aktiv von Foreign Keys anderer Tabellen referenziert wird (oder du Constraints sicher handhaben kannst).&lt;/li&gt;
&lt;li&gt;Du &lt;strong&gt;genug Speicherplatz&lt;/strong&gt; für die temporäre Tabelle hast.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Verwende es nicht, wenn&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Du &lt;strong&gt;Zero Downtime&lt;/strong&gt; brauchst.&lt;/li&gt;
&lt;li&gt;Die Tabelle stark von Foreign Keys referenziert wird, die du nicht anfassen kannst.&lt;/li&gt;
&lt;li&gt;Du DELETE-Trigger &lt;em&gt;zwingend&lt;/em&gt; auslösen musst.&lt;/li&gt;
&lt;li&gt;Du nur eine Minderheit der Zeilen löschst (Chunked Delete kann der einfachere Gewinn sein).&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2&gt;Eine praktische Entscheidungsregel&lt;/h2&gt;
&lt;p&gt;Wenn du einen Satz willst, den du in einem Review sagen kannst:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Wenn das Delete den Großteil der Tabelle entfernen würde, hör auf zu löschen. Bewahren und neu aufbauen.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Oder, wenn du ASCII bevorzugst:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;How much are you deleting?

&amp;lt; 50%     -&gt; chunked DELETE (and index-aware filters)
50–80%    -&gt; measure both approaches
&gt; 80%     -&gt; TRUNCATE + reinsert (if constraints allow)&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;hr&gt;
&lt;h2&gt;Implementierung (SQL)&lt;/h2&gt;
&lt;p&gt;Hier ist die Minimalform:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sql&quot;&gt;&lt;pre class=&quot;language-sql&quot;&gt;&lt;code class=&quot;language-sql&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;-- 1) Preserve the rows you want to keep&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;CREATE&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;TABLE&lt;/span&gt; temp_preserved &lt;span class=&quot;token keyword&quot;&gt;AS&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;FROM&lt;/span&gt; big_table
&lt;span class=&quot;token keyword&quot;&gt;WHERE&lt;/span&gt; preserve_condition&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;-- 2) Nuke the table&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;TRUNCATE&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;TABLE&lt;/span&gt; big_table&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;-- 3) Restore preserved rows&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;INSERT&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;INTO&lt;/span&gt; big_table
&lt;span class=&quot;token keyword&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;FROM&lt;/span&gt; temp_preserved&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;-- 4) Cleanup&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;DROP&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;TABLE&lt;/span&gt; temp_preserved&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Zwei Hinweise, die in Produktion wichtig sind:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;TRUNCATE&lt;/code&gt; ist DDL in MySQL. Es &lt;strong&gt;committet implizit&lt;/strong&gt;, und du kannst es nicht wie eine normale Transaktion zurückrollen.&lt;/li&gt;
&lt;li&gt;Du willst ein Wartungsfenster und ein Backup. Das ist kein &quot;probieren wir live und schauen mal&quot;.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2&gt;Implementierung (Laravel/PHP)&lt;/h2&gt;
&lt;p&gt;Das ist die Version, die ich tatsächlich verwende, wenn ich sie brauche:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;php&quot;&gt;&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-php&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;protected&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;deleteViaTruncateAndReinsert&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;token keyword type-hint&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$connection&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token keyword type-hint&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$tableName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token keyword type-hint&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$preserveCondition&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword return-type&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token variable&quot;&gt;$tempTable&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string double-quoted-string&quot;&gt;&quot;temp_preserved_&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$tableName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;_&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;time&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;// You&apos;re about to do DDL. Be explicit that you&apos;re taking control.&lt;/span&gt;
    &lt;span class=&quot;token class-name static-context&quot;&gt;DB&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;connection&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$connection&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;statement&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string double-quoted-string&quot;&gt;&quot;SET FOREIGN_KEY_CHECKS=0&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name static-context&quot;&gt;DB&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;connection&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$connection&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;statement&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string double-quoted-string&quot;&gt;&quot;
            CREATE TABLE &lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$tempTable&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt; AS
            SELECT * FROM &lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$tableName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
            WHERE &lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$preserveCondition&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
        &quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;token variable&quot;&gt;$preserved&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name static-context&quot;&gt;DB&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;connection&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$connection&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;table&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$tempTable&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;count&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;token class-name static-context&quot;&gt;DB&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;connection&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$connection&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;statement&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string double-quoted-string&quot;&gt;&quot;TRUNCATE TABLE &lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$tableName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;token class-name static-context&quot;&gt;DB&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;connection&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$connection&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;statement&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string double-quoted-string&quot;&gt;&quot;
            INSERT INTO &lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$tableName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
            SELECT * FROM &lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$tempTable&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
        &quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;token class-name static-context&quot;&gt;DB&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;connection&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$connection&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;statement&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string double-quoted-string&quot;&gt;&quot;DROP TABLE &lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$tempTable&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;finally&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name static-context&quot;&gt;DB&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;connection&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$connection&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;statement&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string double-quoted-string&quot;&gt;&quot;SET FOREIGN_KEY_CHECKS=1&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$preserved&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Eine Prise Rubber-Duck-Energie: Lies die Funktion noch einmal und frag dein Zukunfts-Ich -&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&quot;Bin ich &lt;em&gt;sicher&lt;/em&gt;, dass diese Tabelle für einen Moment geleert werden darf?&quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Wenn die Antwort kein eindeutiges Ja ist, ist das nicht das richtige Werkzeug.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;Fallstricke, die du einplanen musst&lt;/h2&gt;
&lt;h3&gt;Auto-increment resets&lt;/h3&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;TRUNCATE&lt;/code&gt; setzt &lt;code class=&quot;language-text&quot;&gt;AUTO_INCREMENT&lt;/code&gt; zurück. Wenn du es erhalten musst:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sql&quot;&gt;&lt;pre class=&quot;language-sql&quot;&gt;&lt;code class=&quot;language-sql&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;MAX&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;id&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;FROM&lt;/span&gt; big_table&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;ALTER&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;TABLE&lt;/span&gt; big_table &lt;span class=&quot;token keyword&quot;&gt;AUTO_INCREMENT&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;max_id &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3&gt;Foreign Keys&lt;/h3&gt;
&lt;p&gt;Wenn andere Tabellen diese hier referenzieren, kann &lt;code class=&quot;language-text&quot;&gt;TRUNCATE&lt;/code&gt; verboten oder unsicher sein. Nicht einfach &quot;Checks deaktivieren&quot; und hoffen.&lt;/p&gt;
&lt;h3&gt;Trigger&lt;/h3&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;TRUNCATE&lt;/code&gt; löst &lt;strong&gt;keine&lt;/strong&gt; DELETE-Trigger aus. Wenn du Trigger-Nebenwirkungen brauchst, bist du wieder bei &lt;code class=&quot;language-text&quot;&gt;DELETE&lt;/code&gt;.&lt;/p&gt;
&lt;h3&gt;Speicherplatz&lt;/h3&gt;
&lt;p&gt;Du brauchst Platz für den erhaltenen Datensatz (temporäre Tabelle). Erst prüfen:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sql&quot;&gt;&lt;pre class=&quot;language-sql&quot;&gt;&lt;code class=&quot;language-sql&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;SELECT&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;ROUND&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;data_length &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1024&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1024&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1024&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;AS&lt;/span&gt; data_gb&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;ROUND&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;index_length &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1024&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1024&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1024&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;AS&lt;/span&gt; index_gb
&lt;span class=&quot;token keyword&quot;&gt;FROM&lt;/span&gt; information_schema&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;tables&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;WHERE&lt;/span&gt; table_schema &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;DATABASE&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token operator&quot;&gt;AND&lt;/span&gt; table_name &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;big_table&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3&gt;Replikation / binlog&lt;/h3&gt;
&lt;p&gt;Das ist DDL + Bulk-Insert. Es kann trotzdem Replica-Lag verursachen. Mach es absichtlich, monitore den Lag, und tu nicht so, als wäre es gratis.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;Wenn du (nahezu) Zero Downtime brauchst&lt;/h2&gt;
&lt;p&gt;Dieser Post handelt vom schnellen Hammer.&lt;/p&gt;
&lt;p&gt;Wenn du ein Skalpell brauchst, nimm die Werkzeuge, die dafür gebaut wurden:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;pt-archiver&lt;/code&gt; (Percona Toolkit) für Batch-Deletes mit replica-freundlichem Pacing&lt;/li&gt;
&lt;li&gt;Partitionierungsstrategien (Partitionen statt Zeilen droppen)&lt;/li&gt;
&lt;li&gt;Shadow-Table-Ansatz + kontrollierter Swap (komplexer, mehr bewegliche Teile)&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2&gt;Schlussgedanke&lt;/h2&gt;
&lt;p&gt;Das ist kein cleverer Trick. Es ist die Entscheidung, für welche Arbeit du bezahlst.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Type-0-Refactoring: Der Schritt vor Schritt eins]]></title><description><![CDATA[Es gibt eine Kategorie von Refactoring, die Teams ständig machen, von der sie sofort profitieren und die fast nie einen Namen bekommt. Es…]]></description><link>https://bdteo.com/de/type-0-refactoring-step-before-step-one/</link><guid isPermaLink="false">https://bdteo.com/de/type-0-refactoring-step-before-step-one/</guid><pubDate>Sat, 13 Dec 2025 12:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Es gibt eine Kategorie von Refactoring, die Teams ständig machen, von der sie sofort profitieren und die fast nie einen Namen bekommt.&lt;/p&gt;
&lt;p&gt;Es ist die Arbeit, die du direkt erledigst, bevor du die unheimliche Datei anfasst. Der Feature-Wunsch zwingt dich in das chaotische Modul. Der Incident landet, und der Bug versteckt sich irgendwo in einer Methode, die wirkt, als hätte sie ihr eigenes Wettersystem.&lt;/p&gt;
&lt;p&gt;Du entwirfst das System nicht neu. Du führst keine neue Abstraktion ein. Du &quot;verbesserst&quot; nichts auf besonders clevere Weise.&lt;/p&gt;
&lt;p&gt;Du machst den Code nur lesbar genug, damit du arbeiten kannst.&lt;/p&gt;
&lt;p&gt;Ich habe angefangen, das &lt;strong&gt;Type-0-Refactoring&lt;/strong&gt; zu nennen.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Type-0-Refactoring&lt;/strong&gt; ist eine vorbereitende, &lt;strong&gt;verhaltenserhaltende Aufräumarbeit&lt;/strong&gt;, die Code leichter nachvollziehbar macht, &lt;strong&gt;bevor&lt;/strong&gt; du Architektur-Refactorings, Performance-Arbeit oder Feature-Arbeit angehst.&lt;/p&gt;
&lt;p&gt;Es ist der Schritt &quot;erst den Boden trocken bekommen, bevor du die Küche umbaust&quot;. Die meisten Teams machen ihn bereits informell. Ihn zu benennen, macht daraus ein gemeinsames Werkzeug.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;Der eigentliche Grund für Type 0: Menschen haben ein begrenztes Arbeitsgedächtnis&lt;/h2&gt;
&lt;p&gt;Hier ist die stumpfe Wahrheit hinter der Idee:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Mein Gehirn (und deins) ist nicht dafür gebaut, unter Zeitdruck verlässlich eine 2000-Zeilen-Methode zu debuggen.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Das ist kein persönlicher Makel. So funktioniert Kognition einfach.&lt;/p&gt;
&lt;p&gt;Debugging verlangt, dass du gleichzeitig im Kopf behältst:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;den aktuellen Ausführungspfad&lt;/li&gt;
&lt;li&gt;den relevanten Zustand&lt;/li&gt;
&lt;li&gt;was jede Variable tatsächlich bedeutet&lt;/li&gt;
&lt;li&gt;die Menge der möglichen Verzweigungen&lt;/li&gt;
&lt;li&gt;die Folgen von &quot;wenn das passiert, dann...&quot;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In kleinem Code ist das beherrschbar.&lt;/p&gt;
&lt;p&gt;In großem Code mit hoher zyklomatischer Komplexität wird daraus probabilistisches Raten. Du kannst immer noch Glück haben, aber es ist teuer und riskant, besonders während eines Hotfixes.&lt;/p&gt;
&lt;p&gt;Type 0 ist eine praktische Antwort: So &lt;strong&gt;kaufst du dir schnell Klarheit&lt;/strong&gt;, ohne die Kosten und Risiken eines &quot;echten Refactorings&quot; zu übernehmen.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;Warum es &quot;Type 0&quot; heißt&lt;/h2&gt;
&lt;p&gt;Der Name kam nicht aus einer großen Theorie. Er kam aus einem Moment mit hohem Druck.&lt;/p&gt;
&lt;p&gt;Ich arbeitete an einem Hotfix. Der Bug war in einer Methode vergraben, die praktisch ihr eigenes kleines Universum war: &lt;strong&gt;ungefähr 2000 Zeilen&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Der Bug war konzeptionell nicht schwer. Die Methode war es.&lt;/p&gt;
&lt;p&gt;Jedes &quot;was passiert, wenn...&quot; verzweigte sich in zehn weitere Fragen, und diese Verzweigung war nicht die nützliche Art. Es war zufällige Komplexität: Lärm, Wiederholung, unklare Benennung und eine Struktur, die nicht zu dem mentalen Modell passte, das man zum Debuggen braucht.&lt;/p&gt;
&lt;p&gt;Was ich brauchte, war keine Perfektion. Ich brauchte &lt;strong&gt;Debuggbarkeit&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;weniger Verzweigungen pro Bildschirm&lt;/li&gt;
&lt;li&gt;klarere &quot;Schritte&quot; mit Namen&lt;/li&gt;
&lt;li&gt;weniger Lärm&lt;/li&gt;
&lt;li&gt;weniger Zeit damit, erneut zu parsen, was ich gerade gelesen hatte&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Aber der Zeitdruck ließ kein größeres Refactoring oder ein &quot;idiomatisches Redesign&quot; zu. Das verantwortlich zu tun, wäre ein halber Tag (oder mehr) gewesen, inklusive manuellem Testen. In einem Hotfix-Fenster ist das keine Disziplin, sondern Zocken.&lt;/p&gt;
&lt;p&gt;Also bat ich ein LLM, Refactoring-Möglichkeiten für die Klasse und diese Methode vorzuschlagen, ohne ihm zu sagen, warum.&lt;/p&gt;
&lt;p&gt;Es kam mit einer Liste von vier &quot;Typen&quot; von Refactoring zurück. Alle sinnvoll. Alle anwendbar. Alle zu teuer für diesen Moment.&lt;/p&gt;
&lt;p&gt;Dann stellte es die höfliche Frage:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&quot;Should I start with Type 1?&quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Da antwortete ich:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&quot;No. Let&apos;s start with Type 0.&quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Und ich definierte Type 0 an Ort und Stelle: eine begrenzte, mechanische Menge von Änderungen, die Komplexität reduzieren und Lesbarkeit erhöhen, &lt;strong&gt;ohne Verhalten oder Architektur zu verändern&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Die Methode wurde navigierbar. Mein Gehirn konnte den Ausführungsfluss wieder verfolgen. Ich fand den Bug, behob ihn und lieferte aus, ohne Kollateralschäden.&lt;/p&gt;
&lt;p&gt;Deshalb mag ich den Namen &lt;strong&gt;Type 0&lt;/strong&gt;: Es ist das Refactoring, das du &lt;strong&gt;vor&lt;/strong&gt; den &quot;echten Refactoring&quot;-Typen machst, besonders wenn du unter Druck stehst und einen sicheren Weg brauchst, schnell Klarheit zu schaffen.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;Das Problem, das Type 0 löst&lt;/h2&gt;
&lt;p&gt;Die meisten Refactoring-Ratschläge setzen voraus, dass du das Design bereits &lt;em&gt;sehen&lt;/em&gt; kannst.&lt;/p&gt;
&lt;p&gt;In echten Codebasen:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;sind Methoden lang und haben mehrere Zwecke&lt;/li&gt;
&lt;li&gt;verbergen wiederholte Ausdrücke und zufällige Komplexität die Absicht&lt;/li&gt;
&lt;li&gt;sind Variablen kryptisch (&lt;code class=&quot;language-text&quot;&gt;$e&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;$tmp&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;$res&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;erzeugen toter Code und unbenutzte Imports mentalen Lärm&lt;/li&gt;
&lt;li&gt;ist die &quot;Form&quot; des Codes so chaotisch, dass selbst kleine Änderungen riskant wirken&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Wenn du darauf &quot;echtes Refactoring&quot; versuchst (Grenzen, Patterns, Verantwortlichkeiten verschieben), stapelst du Unsicherheit auf Unsicherheit:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;du kannst nicht leicht erkennen, welches Verhalten du bewahrst&lt;/li&gt;
&lt;li&gt;du kannst den Blast Radius nicht vorhersagen&lt;/li&gt;
&lt;li&gt;Reviews entgleiten in subjektive Debatten&lt;/li&gt;
&lt;li&gt;Menschen bekommen Angst, Dinge anzufassen, und das Chaos verstärkt sich&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Type 0 senkt zuerst die kognitive Last.&lt;/strong&gt; Es schafft eine stabile Grundlage, auf der tiefere Arbeit sicher passieren kann.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;Greif zu Type 0, wenn...&lt;/h2&gt;
&lt;p&gt;Type 0 ist am wertvollsten, wenn:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;du schnell debuggen musst (Hotfixes, Incidents) und der Code zu groß/verzweigt ist, um sicher darüber nachzudenken&lt;/li&gt;
&lt;li&gt;du dich &quot;in der Methode verloren&quot; fühlst und denselben Abschnitt immer wieder liest, weil die Struktur deinem Arbeitsgedächtnis nicht hilft&lt;/li&gt;
&lt;li&gt;der Code korrekt, aber unlesbar ist, und du es dir nicht leisten kannst, &quot;Logik aufzuräumen&quot;, sondern sie nur freilegen kannst&lt;/li&gt;
&lt;li&gt;du vor tieferer Arbeit Risiko reduzieren willst (du weißt, dass du später refactoren wirst, aber zuerst brauchst du eine klare Karte des aktuellen Verhaltens)&lt;/li&gt;
&lt;li&gt;du Stammeswissen in lesbare Struktur verwandeln willst, damit Debugging nicht von einer Person abhängt&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Type 0 ist kein Luxus. In diesen Fällen ist es oft der schnellste Weg, wieder Kontrolle zu gewinnen.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;Eine Definition, die du in deinem Team benutzen kannst&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Type-0-Refactoring ist eine Menge von Mikro-Refactorings, die Lesbarkeit und Wartbarkeit verbessern, ohne Verhalten oder Architektur zu verändern.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Es ist absichtlich begrenzt. Die Begrenzungen sind das Feature.&lt;/p&gt;
&lt;p&gt;Type 0 besteht aus vier verpflichtenden Sub-Patterns:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;0a. Method extraction&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;0b. Conciseness&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;0c. Empathy (pure readability)&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;0d. Dead code removal&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Und es folgt drei harten Regeln:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Keine Verhaltensänderungen&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Keine Architektur-Änderungen&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Keine &quot;cleveren&quot; Verbesserungen jenseits der vier Patterns&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Wenn du diese Regeln verletzt, machst du nicht mehr Type 0. Du bist in eine andere Arbeitskategorie gewechselt, und die braucht andere Koordination, andere Review-Strenge und oft eine andere Teststrategie.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;Warum überhaupt benennen?&lt;/h2&gt;
&lt;p&gt;Weil Benennung verändert, wie Teams koordinieren.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&quot;Ich mache in diesem PR nur Type 0&quot; sagt Reviewern, worauf sie achten sollen: Verhaltenserhaltung und Lesbarkeit, nicht Architektur-Debatten.&lt;/li&gt;
&lt;li&gt;&quot;Wir brauchen Type 0, bevor wir das refactoren&quot; ist ein ehrliches Eingeständnis, dass der Code noch nicht bereit für tiefere Änderung ist.&lt;/li&gt;
&lt;li&gt;&quot;Lasst uns Type 0 als Schritt 0 machen&quot; schafft ein kleines Ritual, das verhindert, dass du auf Chaos aufbaust.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2&gt;Die vier Sub-Patterns&lt;/h2&gt;
&lt;h3&gt;0a. Method extraction (das Fundament)&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Ziel:&lt;/strong&gt; große Methoden in kleine, fokussierte Methoden zerlegen, damit ein Mensch die Absicht linear lesen kann.&lt;/p&gt;
&lt;p&gt;Faustregeln:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;zerlege Methoden, die zu lang sind, um sie im Arbeitsgedächtnis zu halten&lt;/li&gt;
&lt;li&gt;jede extrahierte Methode sollte eine Sache tun und einen beschreibenden Namen haben&lt;/li&gt;
&lt;li&gt;extrahiere bedeutungsvolle Schritte, keine beliebigen Brocken von N Zeilen&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Warum es funktioniert (besonders beim Debugging):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;kleinere Methoden schaffen Labels für den Ausführungspfad&lt;/li&gt;
&lt;li&gt;ein 2000-Zeilen-Scroll wird zu einer kurzen Orchestrierungsmethode, die du mental durchgehen kannst&lt;/li&gt;
&lt;li&gt;du kannst Breakpoints an semantische Grenzen setzen (&quot;validate input&quot;, &quot;build query&quot;, &quot;apply filters&quot;), statt zu suchen&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;0b. Conciseness (zufällige Komplexität reduzieren)&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Ziel:&lt;/strong&gt; visuellen Lärm entfernen, damit die Absicht hervorsticht.&lt;/p&gt;
&lt;p&gt;Beispiele:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;wiederholte Ausdrücke in lokale Variablen extrahieren&lt;/li&gt;
&lt;li&gt;wiederholte Log-Kontexte / Schlüsselstrings / URL-Fragmente in Variablen extrahieren&lt;/li&gt;
&lt;li&gt;Sprachfeatures bevorzugen, die Absicht direkt kommunizieren&lt;/li&gt;
&lt;li&gt;übermäßig wortreiche Interpolation vereinfachen&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Warum es funktioniert:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;es reduziert kognitive Last&lt;/li&gt;
&lt;li&gt;es macht Diffs kleiner und Änderungen sicherer&lt;/li&gt;
&lt;li&gt;es verhindert Copy/Paste-Drift&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;0c. Empathy (reine Lesbarkeit)&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Ziel:&lt;/strong&gt; für den nächsten Menschen schreiben, nicht für den Compiler.&lt;/p&gt;
&lt;p&gt;Empathy bedeutet:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;beschreibende Variablennamen verwenden (&lt;code class=&quot;language-text&quot;&gt;$e&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;$d&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;$tmp&lt;/code&gt; vermeiden, außer sie sind wirklich offensichtlich)&lt;/li&gt;
&lt;li&gt;konsistente Terminologie in einem Modul beibehalten&lt;/li&gt;
&lt;li&gt;irreführende Namen umbenennen&lt;/li&gt;
&lt;li&gt;Code selbstdokumentierend machen&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Lackmustest:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Wenn jemand das um 2 Uhr nachts während eines Incidents liest, hilft es ihm, den Ausführungspfad im Kopf zu behalten?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;0d. Dead code removal (Lügen entfernen)&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Ziel:&lt;/strong&gt; alles löschen, was vorgibt, wichtig zu sein, es aber nicht ist.&lt;/p&gt;
&lt;p&gt;Beispiele:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;unbenutzte private Methoden&lt;/li&gt;
&lt;li&gt;unbenutzte Imports&lt;/li&gt;
&lt;li&gt;auskommentierte alte Ansätze&lt;/li&gt;
&lt;li&gt;deprecated Helpers, die niemand aufruft&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Warum es funktioniert:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;weniger Code bedeutet weniger Dinge, die man missverstehen kann&lt;/li&gt;
&lt;li&gt;Suchergebnisse werden vertrauenswürdig&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2&gt;Was Type 0 nicht ist&lt;/h2&gt;
&lt;p&gt;Type 0 ist nicht:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Service-Grenzen verändern&lt;/li&gt;
&lt;li&gt;neue Abstraktionen oder Patterns einführen&lt;/li&gt;
&lt;li&gt;einen Workflow neu architektieren&lt;/li&gt;
&lt;li&gt;Libraries ersetzen&lt;/li&gt;
&lt;li&gt;Verantwortlichkeiten über Schichten hinweg neu ordnen&lt;/li&gt;
&lt;li&gt;Logik &quot;fixen&quot;, von der du vermutest, dass sie falsch ist (außer du deklarierst explizit eine Verhaltensänderung und testest sie)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Wenn du dich sagen hörst:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&quot;Wenn ich schon hier bin, können wir auch...&quot;&lt;/li&gt;
&lt;li&gt;&quot;Das wäre schöner, wenn wir...&quot;&lt;/li&gt;
&lt;li&gt;&quot;Wir sollten das wahrscheinlich neu designen...&quot;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;dann verlässt du vielleicht Type 0. Das ist nicht an sich schlecht, aber es sollte absichtlich passieren.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;Das Kernversprechen: Verhaltenserhaltung (und wie du sie wahr hältst)&lt;/h2&gt;
&lt;p&gt;Type 0 funktioniert nur, wenn Teams dem Versprechen vertrauen.&lt;/p&gt;
&lt;p&gt;Und ja, du hast recht, skeptisch zu sein: &lt;strong&gt;Method extraction kann versehentlich Verhalten verändern&lt;/strong&gt; (early returns, Variablenscope, Auswertungsreihenfolge, Exception-Verhalten).&lt;/p&gt;
&lt;p&gt;Deshalb braucht Type 0 Disziplin, die es ehrlich hält:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Extrahiere unverändert, dann benenne um/räume auf.&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Erster Durchlauf: Code in Methoden verschieben, ohne Logik zu verändern&lt;/li&gt;
&lt;li&gt;Zweiter Durchlauf: conciseness + empathy anwenden&lt;/li&gt;
&lt;li&gt;Dritter Durchlauf: toten Code entfernen&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Praktische Leitplanken:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;ordne Bedingungsprüfungen nicht &quot;für die Lesbarkeit&quot; um&lt;/li&gt;
&lt;li&gt;ersetze Logik nicht durch &quot;äquivalente&quot; Logik, außer du bist außerhalb von Type 0&lt;/li&gt;
&lt;li&gt;sei vorsichtig mit Variablen, die vorher in gemeinsamem Scope lagen&lt;/li&gt;
&lt;li&gt;behandle &quot;kleine&quot; Unterschiede im Control Flow als echte Unterschiede&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Und wenn du &lt;em&gt;irgendein&lt;/em&gt; Sicherheitsnetz hast, selbst ein dünnes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;führe einen fokussierten Test aus&lt;/li&gt;
&lt;li&gt;spiele das fehlschlagende Szenario erneut durch&lt;/li&gt;
&lt;li&gt;validiere den einen Pfad, den du berührst&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Bei Type 0 geht es darum, schnell zu sein: &lt;strong&gt;aber schnell durch Reduktion kognitiver Komplexität&lt;/strong&gt;, nicht schnell durch das Überspringen von Sicherheit.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;Type 0 als wiederholbares Team-Ritual&lt;/h2&gt;
&lt;h3&gt;1) Entscheide den Scope (Timeboxing hilft)&lt;/h3&gt;
&lt;p&gt;Beispiele:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&quot;Type 0 the hot path before debugging.&quot;&lt;/li&gt;
&lt;li&gt;&quot;Type 0 only the path touched by this bug fix.&quot;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;2) Identifiziere die &quot;Wirbelsäule&quot; des Codes&lt;/h3&gt;
&lt;p&gt;Finde die Entry-Methode(n) und die Verzweigungspunkte. Verwandle diese Wirbelsäule durch Extraktion in eine lesbare Erzählung.&lt;/p&gt;
&lt;h3&gt;3) Wende die vier Sub-Patterns der Reihe nach an&lt;/h3&gt;
&lt;p&gt;Method extraction → conciseness → empathy → dead code removal.&lt;/p&gt;
&lt;h3&gt;4) Führe eine &quot;Type-0-Checklist&quot; in deinem PR&lt;/h3&gt;
&lt;ul class=&quot;contains-task-list&quot;&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled&gt; Keine Verhaltensänderungen (Inputs/Outputs unverändert)&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled&gt; Keine Architekturbewegungen&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled&gt; Methoden extrahiert und als bedeutungsvolle Schritte benannt&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled&gt; Wiederholte Ausdrücke extrahiert, wo es Klarheit verbessert&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled&gt; Variablen umbenannt; Terminologie konsistent&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled&gt; Toter Code und unbenutzte Imports entfernt&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2&gt;Schlussgedanke&lt;/h2&gt;
&lt;p&gt;Type-0-Refactoring ist das einfachste Versprechen, das ein Entwickler geben kann:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&quot;Ich hinterlasse diesen Code leichter bearbeitbar, als ich ihn vorgefunden habe, ohne zu verändern, was er tut.&quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Manchmal ist das &quot;nice to have&quot;.&lt;/p&gt;
&lt;p&gt;Und manchmal ist es der einzige Weg, wie ein Mensch sich sicher schnell in einem hochkomplexen Chaos bewegen kann, besonders während eines Hotfixes.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Wasch einen Teller mehr: Eine einfache Regel fuer eine dauerhaft saubere Codebase]]></title><description><![CDATA[TL;DR: Behandle jede Aenderung an deiner Codebase wie das Kochen einer Mahlzeit. Du wirst ein paar Teller schmutzig machen. Wenn du fertig…]]></description><link>https://bdteo.com/de/wash-one-more-plate-refactoring-philosophy/</link><guid isPermaLink="false">https://bdteo.com/de/wash-one-more-plate-refactoring-philosophy/</guid><pubDate>Thu, 24 Jul 2025 00:00:00 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;TL;DR&lt;/strong&gt;: Behandle jede Aenderung an deiner Codebase wie das Kochen einer Mahlzeit. Du wirst ein paar Teller schmutzig machen. Wenn du fertig bist, wasch nicht nur die Teller, die du benutzt hast - wasch &lt;em&gt;einen mehr&lt;/em&gt;. Mit der Zeit summiert sich dieser winzige Ueberschuss an Sorgfalt zu einer Kueche (Codebase), die sauber bleibt, statt ins Chaos abzugleiten.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2&gt;Die Metapher: Kochen, Teller und Code&lt;/h2&gt;
&lt;p&gt;Stell dir eine Profikueche vor. Jedes gekochte Gericht macht ein paar Teller schmutzig - selbst in der ordentlichsten Brigade. Stell dir nun vor, dass jeder Koch nach seinem Gericht &lt;em&gt;genau&lt;/em&gt; die Teller waescht, die er schmutzig gemacht hat. Die Kueche wird knapp an der Grenze akzeptabler Sauberkeit bleiben, aber die Entropie kriecht hinein: hier ein bisschen Restschmutz, dort ein fleckiges Schneidebrett. Irgendwann summiert sich das Durcheinander.&lt;/p&gt;
&lt;p&gt;Dreh die Regel jetzt um: Nach dem Kochen waescht jeder Chef &lt;strong&gt;einen Teller mehr, als er schmutzig gemacht hat&lt;/strong&gt;. Langsam wird die Kueche sauberer als vorher - nicht nur erhalten, sondern verbessert. Dasselbe gilt fuer Software: Jede Aufgabe, die du uebernimmst, sollte der Codebase mindestens einen winzigen Ueberschuss an Sauberkeit geben - einen weiteren Test, einen klareren Namen, eine aufgeteilte Funktion, eine tote Dependency weniger. Diese &quot;+1 Teller&quot;-Gewohnheit ist der Weg, wie eine Codebase &lt;em&gt;gesund bleibt&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Ich nenne das &lt;strong&gt;die Wasch-einen-Teller-mehr-Regel&lt;/strong&gt;.&lt;/p&gt;
&lt;h2&gt;Echos aus dem Handwerk: Du bist in guter Gesellschaft&lt;/h2&gt;
&lt;p&gt;Das ist keine einsame Philosophie. Vordenker der Softwarewelt predigen seit Jahrzehnten aehnliche Ideen:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&quot;Always leave the campground cleaner than you found it.&quot;&lt;/strong&gt; Das ist die klassische &lt;a href=&quot;https://deviq.com/principles/boy-scout-rule/&quot;&gt;Boy Scout Rule&lt;/a&gt;, die Robert C. Martin in der Softwarewelt populaer gemacht hat. Derselbe Geist: jedes Mal ein bisschen verbessern.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Technical Debt als Metapher&lt;/strong&gt; (Ward Cunningham): Schulden sammeln Zinsen - ignorier sie, und die &quot;Kueche&quot; kostet morgen mehr in der Benutzung. Wenn du unterwegs etwas davon abtraegst, bleibst du zahlungsfaehig.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Refactoring als kleine, kontinuierliche Schritte&lt;/strong&gt; (Martin Fowler): winzige Aenderungen, die Verhalten erhalten, aber Design verbessern. Kleine Schritte bedeuten geringes Risiko und stetigen Schwung.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&quot;Make it work, make it right, make it fast&quot;&lt;/strong&gt; (Kent Beck): zuerst Korrektheit, dann Sauberkeit, dann Performance. Den zusaetzlichen Teller zu waschen gehoert in die &quot;make it right&quot;-Phase - bevor du vorschnell optimierst.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Broken-Windows-Theorie auf Code angewandt&lt;/strong&gt; (Andrew Hunt &amp;#x26; David Thomas): sichtbares Chaos laedt zu mehr Chaos ein. Ein &quot;Fenster&quot; zu reparieren, bevor es sich ausbreitet, schuetzt die Nachbarschaft (die Codebase).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Diese Ideen verstaerken einander. Sie sagen alle: &lt;em&gt;Gib das Durcheinander nicht weiter; nimm dir einen Moment, um es besser zu machen.&lt;/em&gt;&lt;/p&gt;
&lt;h2&gt;Warum der zusaetzliche Teller zaehlt (auch wenn du beschaeftigt bist)&lt;/h2&gt;
&lt;h3&gt;1. &lt;strong&gt;Entropie ist real&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Sich selbst ueberlassen bleibt Code nicht neutral. Namen driften, Patterns zerfallen, Abstraktionen verrotten. Entropie ist eine Kraft; die einzige Gegenkraft ist konstantes, inkrementelles Aufraeumen. Dein +1 Teller ist Micro-Entropieumkehr.&lt;/p&gt;
&lt;h3&gt;2. &lt;strong&gt;Schulden verzinsen sich schneller, als du denkst&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Die Kosten von Aenderungen wachsen mit jedem &quot;das fixen wir spaeter&quot;. Spaeter kommt selten. Die Zinszahlungen zeigen sich als langsamere Feature-Arbeit, fragile Deployments und Testsuites, denen niemand vertraut. Einen zusaetzlichen Teller &lt;em&gt;heute&lt;/em&gt; zu waschen, senkt morgen den Zinssatz.&lt;/p&gt;
&lt;h3&gt;3. &lt;strong&gt;Das soziale Signal&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Wenn Teamkollegen sehen, dass du hinter dir aufraeumst (und noch ein bisschen mehr), verschiebt sich die Norm. Es wird glaubwuerdig - und erwartet -, Code besser zu hinterlassen, als man ihn vorgefunden hat. Kultur folgt Verhalten.&lt;/p&gt;
&lt;h3&gt;4. &lt;strong&gt;Schwung, nicht Perfektionismus&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Das ist keine Ausrede fuer Yak Shaving. Du baust die Kueche nicht mitten im Service neu. Du faehrst mit dem Schwamm ueber noch einen Teller - klein, sicher und schnell. Genau das haelt Lieferung auf Kurs.&lt;/p&gt;
&lt;h2&gt;Wie man die Wasch-einen-Teller-mehr-Regel praktiziert&lt;/h2&gt;
&lt;p&gt;So verankerst du die Gewohnheit, ohne Scope oder Deadlines zu sprengen.&lt;/p&gt;
&lt;h3&gt;1. Mach &quot;Micro-Refactoring&quot; zu einem Teil von Definition of Done&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Benenne eine verwirrende Variable um.&lt;/li&gt;
&lt;li&gt;Extrahiere eine kleine Funktion, um zyklomatische Komplexitaet zu reduzieren.&lt;/li&gt;
&lt;li&gt;Loesche toten Code oder ungenutzte Imports.&lt;/li&gt;
&lt;li&gt;Fuege einen fehlenden Test fuer einen Bug hinzu, den du gerade gefixt hast.&lt;/li&gt;
&lt;li&gt;Aktualisiere Dokumentation oder einen README-Abschnitt, der dich kurz erschreckt hat.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Das Kriterium: &lt;strong&gt;Wenn es mehr als ein paar Minuten dauert, ist es kein Teller - es ist die ganze Spuelmaschine. Verschieb es.&lt;/strong&gt; Halte es als Ticket fest.&lt;/p&gt;
&lt;h3&gt;2. Nutze Pull Requests als Reinigungsausloeser&lt;/h3&gt;
&lt;p&gt;Jeder PR kann den Zeltplatz sauberer hinterlassen:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Verlange eine &quot;Was hast du gereinigt?&quot;-Checkbox oder eine kurze Notiz.&lt;/li&gt;
&lt;li&gt;Ermutige Reviewer, neben ihrem Review kleine Aufraeumarbeiten zu &lt;em&gt;erbitten&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;Feiere PRs, die diesen Extra-Schliff enthalten (Shout-outs im Standup wirken erstaunlich gut).&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;3. Automatisiere die einfachen Teller&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Pre-commit Hooks fuer Formatierung und Linting.&lt;/li&gt;
&lt;li&gt;Statische Analyse, die komplexe Methoden oder lange Parameterlisten markiert.&lt;/li&gt;
&lt;li&gt;Dependency Checker fuer veraltete Libraries.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Lass automatisierte Besen den trivialen Schmutz wegfegen, damit Menschen sich auf Logik und Design konzentrieren koennen.&lt;/p&gt;
&lt;h3&gt;4. Verankere es in Teamnormen&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Nimm die Regel in euer Working Agreement oder Engineering Handbook auf.&lt;/li&gt;
&lt;li&gt;Tracke Micro-Refactor-Erfolge in Retros, wenn du messbare Belege willst.&lt;/li&gt;
&lt;li&gt;Programmiert gelegentlich im Pair oder Mob, um die Gewohnheit (und den Mut) zu verbreiten.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;5. Wisse, wann du &lt;strong&gt;nicht&lt;/strong&gt; waschen solltest&lt;/h3&gt;
&lt;p&gt;Manchmal brennt die Kueche: Production ist down, oder eine Demo ist nur noch Stunden entfernt. In Notfaellen zerschlag den Stapel schmutziger Teller, wenn es sein muss. Aber komm nach der Krise darauf zurueck. Die Regel ist kein Dogma; sie ist Disziplin.&lt;/p&gt;
&lt;h2&gt;Die Grenze: Ein Teller, nicht das ganze Spuelbecken&lt;/h2&gt;
&lt;p&gt;Scope Creep tarnt sich gern als Handwerkskunst. Deine Aufgabe ist, bei &quot;einem weiteren Teller&quot; aufzuhören. Wenn dieses kleine Refactoring einen tieferen Geruch freilegt, schreib ihn auf und geh weiter. Parke den tieferen Fix:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Erstelle ein Ticket mit dem Label &lt;code class=&quot;language-text&quot;&gt;refactor:&lt;/code&gt; oder &lt;code class=&quot;language-text&quot;&gt;techdebt:&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Verlinke es mit dem relevanten Code, den Tests oder dem Modul.&lt;/li&gt;
&lt;li&gt;Fuege eine kurze Notiz hinzu, warum es wichtig ist.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Du hast deine Pflicht getan: Du hast das Durcheinander gesehen, einen Teller gewaschen und Anweisungen fuer den Rest hinterlassen.&lt;/p&gt;
&lt;h2&gt;Beispiel: Aus einer chaotischen Funktion eine testbare machen&lt;/h2&gt;
&lt;p&gt;Vorher:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;php&quot;&gt;&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-php&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;processOrder&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$order&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$order&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Exception&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&apos;No ID&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token variable&quot;&gt;$tax&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$order&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;country&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token string single-quoted-string&quot;&gt;&apos;BG&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token variable&quot;&gt;$tax&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$order&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;total&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0.20&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$order&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;country&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token string single-quoted-string&quot;&gt;&apos;DE&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token variable&quot;&gt;$tax&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$order&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;total&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0.19&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// Lots more branching...&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// Sends email, writes to DB, calls payment gateway…&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Gewaschener Teller:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;php&quot;&gt;&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-php&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;/**
 * Calculate VAT for an order based on country.
 * Pure function: given (total, country) -&gt; VAT amount.
 */&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;vatFor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword type-hint&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$country&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword type-hint&quot;&gt;float&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$total&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword return-type&quot;&gt;float&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;match&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$country&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token string single-quoted-string&quot;&gt;&apos;BG&apos;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$total&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0.20&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token string single-quoted-string&quot;&gt;&apos;DE&apos;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$total&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0.19&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;default&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0.0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Jetzt ruft deine Hauptfunktion &lt;code class=&quot;language-text&quot;&gt;vatFor()&lt;/code&gt; auf, statt die Logik inline zu halten. Du hast einen Micro-Test fuer &lt;code class=&quot;language-text&quot;&gt;vatFor()&lt;/code&gt; hinzugefuegt. Das ist ein zusaetzlicher Teller - einfach, begrenzt, hilfreich.&lt;/p&gt;
&lt;h2&gt;Schlussgedanken&lt;/h2&gt;
&lt;p&gt;Ein Teller mehr ist winzig. Genau darum geht es. Du brauchst keine heroischen Refactorings, um eine Codebase gesund zu halten; du brauchst eine Kultur kleiner, stetiger Sorgfalt. Mach es zur Gewohnheit, back es in euren Prozess ein, und in einem Jahr wirst du dich fragen, warum deine Kueche &lt;em&gt;keine&lt;/em&gt; Katastrophe ist - weil du nie zugelassen hast, dass sie eine wird.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;strong&gt;Call to Action&lt;/strong&gt;: Wenn du das naechste Mal eine Datei beruehrst, frag dich: &lt;em&gt;&quot;Welchen zusaetzlichen Teller kann ich waschen, bevor ich diese Aenderung committe?&quot;&lt;/em&gt; Dann tu es. Wiederholen. Kultur veraendern, ein makelloser Teller nach dem anderen.&lt;/p&gt;
&lt;h3&gt;Quellen &amp;#x26; Weiterfuehrende Lektuere&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Robert C. Martin (&quot;Uncle Bob&quot;) - Boy Scout Rule:&lt;/strong&gt; &quot;&lt;a href=&quot;https://97-things-every-x-should-know.gitbooks.io/97-things-every-programmer-should-know/content/en/thing_08/&quot;&gt;The Boy Scout Rule&lt;/a&gt;&quot; aus &lt;em&gt;97 Things Every Programmer Should Know&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Ward Cunningham - Technical Debt Metaphor:&lt;/strong&gt; Cunninghams urspruengliche Erklaerung von &lt;a href=&quot;https://martinfowler.com/bliki/TechnicalDebt.html&quot;&gt;Technical Debt&lt;/a&gt; auf Martin Fowlers Website.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Martin Fowler - Continuous Micro-Refactoring:&lt;/strong&gt; Fowlers Buch &lt;a href=&quot;https://martinfowler.com/books/refactoring.html&quot;&gt;&lt;em&gt;Refactoring: Improving the Design of Existing Code&lt;/em&gt;&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Kent Beck - &quot;Make it work, make it right, make it fast&quot;:&lt;/strong&gt; Eine Erklaerung des Mantras von &lt;a href=&quot;https://ronjeffries.com/articles/-x024/biot/-bv40/3/&quot;&gt;Ron Jeffries&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Andrew Hunt &amp;#x26; David Thomas - Broken Windows in Software:&lt;/strong&gt; Das Konzept wird in ihrem Buch &lt;a href=&quot;https://en.wikipedia.org/wiki/The_Pragmatic_Programmer&quot;&gt;&lt;em&gt;The Pragmatic Programmer&lt;/em&gt;&lt;/a&gt; beschrieben.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Software Entropy &amp;#x26; Maintenance:&lt;/strong&gt; Eine gute Lektuere zum Thema ist &quot;&lt;a href=&quot;https://chroniclesofapragmaticprogrammer.substack.com/p/entropy-in-software-and-the-broken-window&quot;&gt;Entropy in Software and the Broken Window Theory&lt;/a&gt;.&quot;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[PHP 8.5: Eine Tour durch die kommenden Features]]></title><description><![CDATA[TL;DR-Hype-Leiter (mein spielerisches Ranking) Pipe-Operator () - Lesbarer, linearer Transformationsgenuss. Refactoring-Magnet. Attribut…]]></description><link>https://bdteo.com/de/php-8-5-new-features-pipe-operator-guide/</link><guid isPermaLink="false">https://bdteo.com/de/php-8-5-new-features-pipe-operator-guide/</guid><pubDate>Sun, 20 Jul 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;TL;DR-Hype-Leiter (mein spielerisches Ranking)&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Pipe-Operator (&lt;code class=&quot;language-text&quot;&gt;|&gt;&lt;/code&gt;)&lt;/strong&gt; - Lesbarer, linearer Transformationsgenuss. &lt;em&gt;Refactoring-Magnet.&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Attribut &lt;code class=&quot;language-text&quot;&gt;#[\NoDiscard]&lt;/code&gt;&lt;/strong&gt; - Macht aus &quot;Rückgabewert vergessen&quot; eine &lt;em&gt;sofortige&lt;/em&gt; Warnung. Passt wunderbar zu Pipes.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Statische Closures / First-Class Callables in Konstantenausdrücken&lt;/strong&gt; - Strategy-Maps und Attributargumente zur Compile-Zeit. Framework-Süßigkeit.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code class=&quot;language-text&quot;&gt;php --ini=diff&lt;/code&gt;&lt;/strong&gt; - Sofortiger Diff für Environment-Drift. Rettet dich vor Config-Höhlenforschung.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Attribute auf globalen und Klassenkonstanten&lt;/strong&gt; - Metadaten überall (Flags, Deprecations, semantische Tags).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code class=&quot;language-text&quot;&gt;array_first()&lt;/code&gt; / &lt;code class=&quot;language-text&quot;&gt;array_last()&lt;/code&gt;&lt;/strong&gt; - Offensichtlich, absichtsvoll, nicht-mutierend. Tschüss, &lt;code class=&quot;language-text&quot;&gt;reset()&lt;/code&gt;-Nebenwirkungen.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code class=&quot;language-text&quot;&gt;get_exception_handler()&lt;/code&gt; und Freunde&lt;/strong&gt; - Introspektion für geschichtetes Error-Handling (Gewinn auf Framework-/Infra-Ebene).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Intl-Goodies (&lt;code class=&quot;language-text&quot;&gt;IntlListFormatter&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;Locale::isRightToLeft()&lt;/code&gt;)&lt;/strong&gt; - Glattere, lokalisierte UX mit fast keinem Code.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Graphem-bewusstes Levenshtein&lt;/strong&gt; - User-facing Fuzzy Matching, das menschliche Zeichen tatsächlich respektiert.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Directory Object + cURL / Build-Introspektion / Sonstiges&lt;/strong&gt; - Konsistenz- und Operability-Politur.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;(Ja, &lt;em&gt;deine&lt;/em&gt; Reihenfolge kann anders sein. Genau das macht Spaß - streitet bei Kaffee darüber.)&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;1. Pipe-Operator (&lt;code class=&quot;language-text&quot;&gt;|&gt;&lt;/code&gt;) - &quot;Easy Peasy Lemon Squeezy&quot;&lt;/h2&gt;
&lt;p&gt;Verschachtelte Aufrufe und temporäre Wegwerfvariablen? Weg. Der Pipe-Operator nimmt den Wert links und füttert ihn als &lt;em&gt;erstes Argument&lt;/em&gt; in das Callable rechts. Du liest von oben nach unten, die Logik fließt wie Prosa, und die Absicht springt dir ins Gesicht.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Vorher (Variablen-Hüpfspiel):&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;php&quot;&gt;&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-php&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$email&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$request&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token keyword type-declaration&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&apos;email&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token variable&quot;&gt;$email&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;trim&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$email&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token variable&quot;&gt;$email&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;strtolower&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$email&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;sendEmail&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$email&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Vorher (Klammernest):&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;php&quot;&gt;&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-php&quot;&gt;&lt;span class=&quot;token function&quot;&gt;sendEmail&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;strtolower&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;trim&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$request&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token keyword type-declaration&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&apos;email&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Nachher (Pipe-Zen):&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;php&quot;&gt;&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-php&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$request&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token keyword type-declaration&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&apos;email&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;trim&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;strtolower&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;sendEmail&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Warum das zählt:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;Visueller Datenfluss.&lt;/em&gt; Kein mentaler Stapel verschachtelter Rückgaben.&lt;/li&gt;
&lt;li&gt;Kombiniert sich wunderbar mit kleinen, reinen Helfern.&lt;/li&gt;
&lt;li&gt;Ermutigt dazu, Transformationen in benannte Funktionen / Closures zu zerlegen.&lt;/li&gt;
&lt;li&gt;Erfüllt &lt;code class=&quot;language-text&quot;&gt;#[\NoDiscard]&lt;/code&gt; automatisch, weil der Wert weiterwandert.&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Style-Tipp:&lt;/strong&gt; Halte jede Stufe frei von Side-Effects; reserviere die &lt;em&gt;letzte&lt;/em&gt; Pipe für einen Effekt (z. B. persistieren, senden, emitten), damit du visuell erkennst, wo &quot;Reinheit&quot; endet.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2&gt;2. &lt;code class=&quot;language-text&quot;&gt;#[\NoDiscard]&lt;/code&gt; - Absicht mit Zähnen&lt;/h2&gt;
&lt;p&gt;Wie viele subtile Bugs waren bloß: &quot;Wir haben das Ding aufgerufen, aber vergessen, den Rückgabewert zu benutzen&quot;? Markiere eine Funktion oder Methode mit &lt;code class=&quot;language-text&quot;&gt;#[\NoDiscard]&lt;/code&gt;, um zu verlangen, dass ihr Ergebnis &lt;em&gt;verwendet&lt;/em&gt; wird - oder bewusst per &lt;code class=&quot;language-text&quot;&gt;(void)&lt;/code&gt;-Cast ignoriert.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;php&quot;&gt;&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-php&quot;&gt;&lt;span class=&quot;token attribute&quot;&gt;&lt;span class=&quot;token delimiter punctuation&quot;&gt;#[&lt;/span&gt;&lt;span class=&quot;token attribute-content&quot;&gt;\&lt;span class=&quot;token attribute-class-name class-name&quot;&gt;NoDiscard&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string double-quoted-string&quot;&gt;&quot;Token must be used – did you forget to persist or dispatch?&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token delimiter punctuation&quot;&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;issueAuthToken&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name type-declaration&quot;&gt;User&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$user&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword return-type&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;generateTokenFor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$user&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token function&quot;&gt;issueAuthToken&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$user&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// ⚠ Emits warning in 8.5&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword type-declaration&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;issueAuthToken&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$user&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// Explicit intentional discard&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Patterns:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Result-Objekte (&lt;code class=&quot;language-text&quot;&gt;Result&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;Outcome&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;ValidationReport&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Immutable Builder (geben bei jedem Aufruf eine neue Instanz zurück).&lt;/li&gt;
&lt;li&gt;Security- / Side-Effect-Gates (Tokens, Signaturen).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Synergie:&lt;/strong&gt; In einer Pipeline wird die Rückgabe jeder Stufe von der nächsten zwangsläufig konsumiert, also verschwinden versehentliche Discards.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;3. Statische Closures in Konstantenausdrücken - &lt;em&gt;&quot;Moment... was?!&quot;&lt;/em&gt;&lt;/h2&gt;
&lt;p&gt;Du kannst jetzt &lt;strong&gt;statische&lt;/strong&gt; Closures (oder First-Class Callables) in Konstantenausdrücke, Default-Property-Werte, Attributargumente und Default-Parameter-Arrays einbetten. Denk an Compile-Time-Registries ohne Boot-Time-Verkabelungsakrobatik.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;php&quot;&gt;&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-php&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name-definition class-name&quot;&gt;Sanitizers&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;STAGES&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
        &lt;span class=&quot;token string single-quoted-string&quot;&gt;&apos;trim&apos;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;trim&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token string single-quoted-string&quot;&gt;&apos;upper&apos;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword type-hint&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$v&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword return-type&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;strtoupper&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$v&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// Attribute example&lt;/span&gt;
&lt;span class=&quot;token attribute&quot;&gt;&lt;span class=&quot;token delimiter punctuation&quot;&gt;#[&lt;/span&gt;&lt;span class=&quot;token attribute-content&quot;&gt;&lt;span class=&quot;token attribute-class-name class-name&quot;&gt;Validate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;token attribute-class-name class-name&quot;&gt;rules&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
        &lt;span class=&quot;token string single-quoted-string&quot;&gt;&apos;title&apos;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token attribute-class-name class-name&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token attribute-class-name class-name&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token attribute-class-name class-name&quot;&gt;string&lt;/span&gt; $&lt;span class=&quot;token attribute-class-name class-name&quot;&gt;v&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token attribute-class-name class-name&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token attribute-class-name class-name&quot;&gt;mb_strlen&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;$&lt;span class=&quot;token attribute-class-name class-name&quot;&gt;v&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token string single-quoted-string&quot;&gt;&apos;slug&apos;&lt;/span&gt;  &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token attribute-class-name class-name&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token attribute-class-name class-name&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token attribute-class-name class-name&quot;&gt;string&lt;/span&gt; $&lt;span class=&quot;token attribute-class-name class-name&quot;&gt;v&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token attribute-class-name class-name&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token attribute-class-name class-name&quot;&gt;preg_match&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&apos;/^[a-z0-9-]+$/&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; $&lt;span class=&quot;token attribute-class-name class-name&quot;&gt;v&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token delimiter punctuation&quot;&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name-definition class-name&quot;&gt;Article&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Warum das knallt:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Beseitigt Service-Locator-Lookups für einfache Strategien.&lt;/li&gt;
&lt;li&gt;Schiebt reine Mapping-Tabellen in Konstanten (immutable + cachebar).&lt;/li&gt;
&lt;li&gt;Attribute können Logik jetzt &lt;em&gt;direkt&lt;/em&gt; kapseln - nicht nur skalare Metadaten.&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Einschränkung:&lt;/strong&gt; Muss &lt;code class=&quot;language-text&quot;&gt;static&lt;/code&gt; sein; kein &lt;code class=&quot;language-text&quot;&gt;$this&lt;/code&gt;, kein Capturing von Variablen. Wenn du Kontext brauchst, gib ihn später explizit mit.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2&gt;4. &lt;code class=&quot;language-text&quot;&gt;php --ini=diff&lt;/code&gt; - Röntgenblick für Config-Drift&lt;/h2&gt;
&lt;p&gt;Müde von &lt;em&gt;&quot;Aber auf Staging funktioniert es&quot;&lt;/em&gt;? Dieses CLI-Flag gibt nur die INI-Direktiven aus, die vom Default abweichen.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;php &lt;span class=&quot;token parameter variable&quot;&gt;--ini&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;diff
&lt;span class=&quot;token comment&quot;&gt;# memory_limit: &quot;128M&quot; -&gt; &quot;-1&quot;&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;# max_execution_time: &quot;30&quot; -&gt; &quot;0&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Use Cases:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;CI-Step, um eine konsistente Baseline durchzusetzen.&lt;/li&gt;
&lt;li&gt;Schneller Sanity Check, wenn ein Worker sich merkwürdig verhält.&lt;/li&gt;
&lt;li&gt;Triage von Memory-/Time-Anomalien.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Pro-Tipp: Capture den Output in Version Control für Runtime-Baselines.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;5. Attribute auf globalen und Klassenkonstanten - Metadaten überall&lt;/h2&gt;
&lt;p&gt;Konstanten steigen vom &quot;dummen Wert&quot; zum &quot;annotierten Teilnehmer&quot; auf. Dekoriere Domain-Flags, Feature-Toggles, Deprecation-Hinweise, Unit-Semantik - &lt;em&gt;direkt an der Definitionsstelle.&lt;/em&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;php&quot;&gt;&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-php&quot;&gt;&lt;span class=&quot;token attribute&quot;&gt;&lt;span class=&quot;token delimiter punctuation&quot;&gt;#[&lt;/span&gt;&lt;span class=&quot;token attribute-content&quot;&gt;&lt;span class=&quot;token attribute-class-name class-name&quot;&gt;Deprecated&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string double-quoted-string&quot;&gt;&quot;Use FEATURE_NEW_PRICING instead&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token delimiter punctuation&quot;&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;FEATURE_OLD_PRICING&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token attribute&quot;&gt;&lt;span class=&quot;token delimiter punctuation&quot;&gt;#[&lt;/span&gt;&lt;span class=&quot;token attribute-content&quot;&gt;&lt;span class=&quot;token attribute-class-name class-name&quot;&gt;Unit&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string double-quoted-string&quot;&gt;&quot;ms&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token delimiter punctuation&quot;&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;DEFAULT_TIMEOUT&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;250&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Framework-Hebel:&lt;/strong&gt; Deprecations automatisch entdecken, Feature-Kataloge füttern, Docs generieren oder Policies per Reflection erzwingen.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;6. &lt;code class=&quot;language-text&quot;&gt;array_first()&lt;/code&gt; / &lt;code class=&quot;language-text&quot;&gt;array_last()&lt;/code&gt; - Das Offensichtliche existiert endlich&lt;/h2&gt;
&lt;p&gt;Hör auf, Pointer-Akrobatik (&lt;code class=&quot;language-text&quot;&gt;reset()&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;end()&lt;/code&gt;) zu machen oder Arrays zu slicen, nur um hineinzuschauen. Diese Helfer lesen sich direkt nach Absicht und mutieren den internen Array-Zustand &lt;em&gt;nicht&lt;/em&gt;.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;php&quot;&gt;&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-php&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$firstUser&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;array_first&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$users&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token variable&quot;&gt;$lastUser&lt;/span&gt;  &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;array_last&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$users&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Refactor-Pattern:&lt;/strong&gt; Grep nach &lt;code class=&quot;language-text&quot;&gt;reset(&lt;/code&gt; / &lt;code class=&quot;language-text&quot;&gt;end(&lt;/code&gt; / kompliziertem &lt;code class=&quot;language-text&quot;&gt;array_slice(..., 0, 1)&lt;/code&gt; - ersetze es durch semantische Calls. Sauberere Diffs, weniger Mikro-Bugs.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;7. &lt;code class=&quot;language-text&quot;&gt;get_exception_handler()&lt;/code&gt; (und bessere Fatal Traces) - Observability-Upgrade&lt;/h2&gt;
&lt;p&gt;Framework- / Infra-Entwickler dürfen sich freuen: Du kannst jetzt den aktiven Exception Handler introspektieren. Chainen, wrappen, wiederherstellen oder dekorieren, ohne brüchiges globales Jonglieren.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;php&quot;&gt;&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-php&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$previous&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;get_exception_handler&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;set_exception_handler&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name type-declaration&quot;&gt;Throwable&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$e&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$previous&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;logToSentry&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$e&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$previous&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$previous&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$e&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Gekoppelt mit reicheren Stack Traces bei Fatal Errors beschleunigt das Production-Postmortems dramatisch.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;8. Intl-Erweiterungen - menschenfreundliche Listen und Richtung&lt;/h2&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;IntlListFormatter&lt;/code&gt; rendert charmante, locale-bewusste Konjunktionen/Disjunktionen ohne handgerollte Klebelogik.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;php&quot;&gt;&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-php&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$f&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;IntlListFormatter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&apos;pt_PT&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string single-quoted-string&quot;&gt;&apos;conjunction&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$f&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&apos;Lisboa&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string single-quoted-string&quot;&gt;&apos;Porto&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string single-quoted-string&quot;&gt;&apos;Coimbra&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// &quot;Lisboa, Porto e Coimbra&quot;&lt;/span&gt;

&lt;span class=&quot;token variable&quot;&gt;$fOr&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;IntlListFormatter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&apos;en_US&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string single-quoted-string&quot;&gt;&apos;disjunction&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$fOr&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&apos;apples&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string single-quoted-string&quot;&gt;&apos;bananas&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string single-quoted-string&quot;&gt;&apos;cherries&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// &quot;apples, bananas, or cherries&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Kombiniere das mit &lt;code class=&quot;language-text&quot;&gt;Locale::isRightToLeft()&lt;/code&gt; (oder &lt;code class=&quot;language-text&quot;&gt;locale_is_right_to_left()&lt;/code&gt;), um die Layout-Richtung automatisch umzuschalten.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;9. Graphem-bewusstes Levenshtein - echte String-Distanz für Nutzer&lt;/h2&gt;
&lt;p&gt;Wenn Nutzer Emoji, Akzente oder kombinierende Zeichen tippen, lügen Byte-Distanz oder naive Codepoint-Distanz. &lt;code class=&quot;language-text&quot;&gt;grapheme_levenshtein()&lt;/code&gt; respektiert &lt;strong&gt;sichtbare&lt;/strong&gt; Zeichen.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;php&quot;&gt;&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-php&quot;&gt;&lt;span class=&quot;token function&quot;&gt;grapheme_levenshtein&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&apos;café&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string single-quoted-string&quot;&gt;&apos;cafe&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 0 – visually same after accent&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Suchvorschläge, Fuzzy Match und typo-tolerante Login-Flows werden sprachlich fair.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;10. Die Politur-Parade&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Directory Object:&lt;/strong&gt; &lt;code class=&quot;language-text&quot;&gt;opendir()&lt;/code&gt; gibt dir jetzt ein echtes Objekt (Type Safety, künftige Erweiterbarkeit) statt einer Legacy-Resource.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;cURL-Verbesserungen:&lt;/strong&gt; Bessere Share Handles + Multi-Handle-Introspektion = bessere Connection Reuse in langlebigen Workern (denk an RoadRunner, Swoole) und feineres Performance-Tuning.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;code class=&quot;language-text&quot;&gt;PHP_BUILD_DATE&lt;/code&gt;:&lt;/strong&gt; Schneller &quot;Wie alt ist dieses Binary?&quot;-Check für Audit-Scripts. Großartig, um sicherzustellen, dass Fleet Nodes nicht still hinterherhinken.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;Feature-Synergie-Cheat-Sheet&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Ziel&lt;/th&gt;
&lt;th&gt;Kombiniere&lt;/th&gt;
&lt;th&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Transformationspipeline mit erzwungener Nutzung&lt;/td&gt;
&lt;td&gt;`&lt;/td&gt;
&lt;td&gt;&gt;&lt;code class=&quot;language-text&quot;&gt;+&lt;/code&gt;#[\NoDiscard]`&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Deklarative Validation / Strategy-Maps&lt;/td&gt;
&lt;td&gt;Statische Closures in Konstantenausdrücken + Konstantenattribute&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Sicherere Refactors von Legacy-Arrays&lt;/td&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;array_first()/array_last()&lt;/code&gt; + strikte Return Types&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Production-Incident-Triage&lt;/td&gt;
&lt;td&gt;Bessere Fatal Stack Traces + &lt;code class=&quot;language-text&quot;&gt;php --ini=diff&lt;/code&gt; + &lt;code class=&quot;language-text&quot;&gt;get_exception_handler()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Internationaler UX-Feinschliff&lt;/td&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;IntlListFormatter&lt;/code&gt; + Richtungserkennung + Graphem-Distanz&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr&gt;
&lt;h2&gt;Praktischer Einführungsplan&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Pipe-Operator schrittweise einführen&lt;/strong&gt;: Starte in reinen Daten-Normalisierungsschichten; erzwinge Style (ein Side-Effect am Ende) im Code Review.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Kritische APIs mit &lt;code class=&quot;language-text&quot;&gt;#[\NoDiscard]&lt;/code&gt; annotieren&lt;/strong&gt;: Konzentriere dich zuerst auf Security, Persistenz und Builder - miss Warning Counts in CI.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Strategy Tables refactoren&lt;/strong&gt;: Verschiebe einfache Callable-Maps in &lt;code class=&quot;language-text&quot;&gt;public const&lt;/code&gt;-Arrays mit statischen Closures für Null-Boot-Kosten.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Config-Drift-Checks&lt;/strong&gt;: Füge einen CI-Job hinzu, der &lt;code class=&quot;language-text&quot;&gt;php --ini=diff&lt;/code&gt;-Output captured; alarmiere bei unerwarteten Änderungen.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Metadaten-Sweep&lt;/strong&gt;: Tagge Konstanten mit Deprecation / Units / Feature Flags, um internes Tooling zu füttern.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Array-Edge-Extraction-Cleanup&lt;/strong&gt;: Codemod, um Pointer-manipulierende Patterns zu ersetzen.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Error-Handler-Layering&lt;/strong&gt;: Wrappe bestehende globale Handler mit &lt;code class=&quot;language-text&quot;&gt;get_exception_handler()&lt;/code&gt; für Observability (Sentry/New-Relic-Instrumentierung).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;i18n-Erweiterungen&lt;/strong&gt;: Ersetze manuellen &quot;List Glue&quot;-Code durch &lt;code class=&quot;language-text&quot;&gt;IntlListFormatter&lt;/code&gt;; teste automatische RTL-Layoutauswahl.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Fuzzy-Matching-Qualität&lt;/strong&gt;: Wo nutzergenerierter multilingualer Text auftaucht (Suche, Tagging), benchmarke Graphem- gegen klassische Distanz.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Runtime-Audit-Script&lt;/strong&gt;: Logge &lt;code class=&quot;language-text&quot;&gt;PHP_BUILD_DATE&lt;/code&gt; + &lt;code class=&quot;language-text&quot;&gt;php --ini=diff&lt;/code&gt; täglich, um alternde Container zu erkennen.&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h2&gt;Fallstricke &amp;#x26; Gotchas&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Item&lt;/th&gt;
&lt;th&gt;Worauf du achten musst&lt;/th&gt;
&lt;th&gt;Mitigation&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Pipe-Operator-Missbrauch&lt;/td&gt;
&lt;td&gt;Side-Effects mitten in der Pipeline&lt;/td&gt;
&lt;td&gt;Auf reine Funktionen bis zur letzten Stufe beschränken&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;#[\NoDiscard]&lt;/code&gt;-Overuse&lt;/td&gt;
&lt;td&gt;Noise Fatigue (Warnungsblindheit)&lt;/td&gt;
&lt;td&gt;Nur auf &lt;em&gt;semantisch kritische&lt;/em&gt; Rückgaben anwenden&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Grenzen statischer Closures&lt;/td&gt;
&lt;td&gt;Captured Context nötig&lt;/td&gt;
&lt;td&gt;Kontext als expliziten Param übergeben oder Factory nutzen&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Konstantenattribute-Wildwuchs&lt;/td&gt;
&lt;td&gt;Metadaten-Fragmentierung&lt;/td&gt;
&lt;td&gt;Interne Namenskonventionen für Attribute etablieren&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;i18n-Listenformatierung&lt;/td&gt;
&lt;td&gt;Angenommener Interpunktionsstil&lt;/td&gt;
&lt;td&gt;Snapshot-Tests pro Locale&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr&gt;
&lt;h2&gt;&quot;Show Me&quot; Mini-Playground&lt;/h2&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;php&quot;&gt;&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-php&quot;&gt;&lt;span class=&quot;token attribute&quot;&gt;&lt;span class=&quot;token delimiter punctuation&quot;&gt;#[&lt;/span&gt;&lt;span class=&quot;token attribute-content&quot;&gt;&lt;span class=&quot;token attribute-class-name class-name&quot;&gt;NoDiscard&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string double-quoted-string&quot;&gt;&quot;Hash must be stored or compared&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token delimiter punctuation&quot;&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;password_hash_safe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword type-hint&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$plain&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword return-type&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;password_hash&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$plain&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;PASSWORD_DEFAULT&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;sanitize_email&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword type-hint&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$raw&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword return-type&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;strtolower&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;trim&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$raw&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token variable&quot;&gt;$request&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token keyword type-declaration&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&apos;email&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;sanitize_email&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;fn&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$email&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;strlen&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$email&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$email&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;InvalidArgumentException&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&apos;Too short&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;sendEmail&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// Each stage consumes prior result – no discard.&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;php&quot;&gt;&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-php&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name-definition class-name&quot;&gt;Rules&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;VALIDATORS&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
        &lt;span class=&quot;token string single-quoted-string&quot;&gt;&apos;title&apos;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword type-hint&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$v&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$v&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;!==&lt;/span&gt; &lt;span class=&quot;token string single-quoted-string&quot;&gt;&apos;&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token string single-quoted-string&quot;&gt;&apos;slug&apos;&lt;/span&gt;  &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword type-hint&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$v&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword type-casting&quot;&gt;bool&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;preg_match&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&apos;/^[a-z0-9-]+$/&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$v&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;foreach&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name static-context&quot;&gt;Rules&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;VALIDATORS&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$field&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$check&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$check&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$data&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$field&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;??&lt;/span&gt; &lt;span class=&quot;token string single-quoted-string&quot;&gt;&apos;&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RuntimeException&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string double-quoted-string&quot;&gt;&quot;Invalid &lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$field&lt;/span&gt;&lt;/span&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;hr&gt;
&lt;h2&gt;Wann du &lt;strong&gt;nicht&lt;/strong&gt; nach dem glänzenden Ding greifen solltest&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Eine einzelne triviale Transformation?&lt;/strong&gt; Eine Pipe könnte Overkill sein; &lt;code class=&quot;language-text&quot;&gt;strtolower($x)&lt;/code&gt; ist weiterhin okay.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Kontextlastige Closures?&lt;/strong&gt; Reguläre Methoden mit Dependency Injection &gt; statische Closure-Hacks.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Legacy-Codebase mitten im Upgrade?&lt;/strong&gt; Führe ein Feature nach dem anderen ein, um kognitive Unruhe zu vermeiden.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2&gt;Mental-Model-Recap&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;Zentrales mentales Modell&lt;/th&gt;
&lt;th&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;`&lt;/td&gt;
&lt;td&gt;&gt;`&lt;/td&gt;
&lt;td&gt;Lineares Value Threading; Nesting &amp;#x26; Temp-Vars eliminieren&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;#[\NoDiscard]&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Absichtsvollen&lt;/em&gt; Konsum erzwingen (verwenden oder per &lt;code class=&quot;language-text&quot;&gt;(void)&lt;/code&gt; ignorieren)&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Statische Closure-Konstanten&lt;/td&gt;
&lt;td&gt;Immutable Strategy Registry, zur Load-Time vorbereitet&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Attribute auf Konstanten&lt;/td&gt;
&lt;td&gt;First-Class-Metadatenkanal für Tooling &amp;#x26; Policies&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;array_first()/last()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Deklarativer, nicht-mutierender Zugriff auf Ränder&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;php --ini=diff&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Config-Delta-Linse gegenüber Default-Baseline&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;get_exception_handler()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Globalen Exception Flow introspektieren &amp;#x26; wrappen&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Intl-Erweiterungen&lt;/td&gt;
&lt;td&gt;Eingebaute Locale-Intelligenz statt handgemachtem Kleber&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Graphem-Distanz&lt;/td&gt;
&lt;td&gt;Operationen auf menschlich wahrgenommenen Zeichen statt rohen Codepoints&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Build- &amp;#x26; Resource-Politur&lt;/td&gt;
&lt;td&gt;Inkrementelle Standardisierung &amp;#x26; Introspektion&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr&gt;
&lt;h2&gt;Final Vibes&lt;/h2&gt;
&lt;p&gt;PHP 8.5 schreit nicht mit Paradigmenwechseln - es &lt;em&gt;flüstert&lt;/em&gt; unerbittliche Ergonomiegewinne. Allein die Kombination aus Pipe-Operator + &lt;code class=&quot;language-text&quot;&gt;#[\NoDiscard]&lt;/code&gt; wird deinen Code in Richtung klarerer Absicht schubsen. Streu Compile-Time-Closures und Konstantenattribute darüber, und deine Frameworks/Components fühlen sich deklarativer, expliziter, auffindbarer an. Bam bam boom - ship it.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Du bist dran:&lt;/strong&gt; Such dir ein Feature aus (wahrscheinlich die Pipe), wende es chirurgisch in einem kleinen Modul an, miss die Klarheit im Code-Review-Feedback, dann erweitere. Momentum schlägt Big-Bang-Rewrites.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Bleib spielerisch, refactore mutig, und - ja - schreib deinen Taylors, wenn du die &quot;Moment, WAS?!&quot;-Momente findest.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Happy Coding.&lt;/strong&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Was ist "gute" Code Coverage? Ein Leitfaden aus der Praxis]]></title><description><![CDATA[Was ist "gute" Code Coverage? Mein Praxisleitfaden, um Bugs zu stoppen, ohne Engineering-Zeit zu verbrennen Jedes Mal, wenn ich  oder…]]></description><link>https://bdteo.com/de/what-is-good-code-coverage-real-world-guide/</link><guid isPermaLink="false">https://bdteo.com/de/what-is-good-code-coverage-real-world-guide/</guid><pubDate>Tue, 15 Jul 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;Was ist &quot;gute&quot; Code Coverage? Mein Praxisleitfaden, um Bugs zu stoppen, ohne Engineering-Zeit zu verbrennen&lt;/h1&gt;
&lt;p&gt;Jedes Mal, wenn ich &lt;code class=&quot;language-text&quot;&gt;npm run coverage&lt;/code&gt; oder &lt;code class=&quot;language-text&quot;&gt;phpunit --coverage&lt;/code&gt; ausführe, taucht dieselbe Frage auf:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;&quot;Okay ... 74 %. Reicht das?&quot;&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Die Softwareentwicklungs-Blogosphäre ruft &quot;100 % oder gar nichts!&quot; Währenddessen erinnert mich &lt;a href=&quot;https://launchdarkly.com/blog/code-coverage-what-it-is-and-why-it-matters/&quot; target=&quot;_blank&quot;&gt;launchdarkly.com&lt;/a&gt; höflich daran, dass 100 % ausgeführt nicht 100 % getestet bedeutet.&lt;br&gt;
Ich habe Wochen damit verbracht, dieser glänzenden Kennzahl hinterherzulaufen, und noch mehr Wochen damit, &lt;em&gt;andere&lt;/em&gt; Probleme zu debuggen. Das hier ist der praxiserprobte Mittelweg, bei dem ich gelandet bin.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;Warum 100 % Coverage eine Fata Morgana sind&lt;/h2&gt;
&lt;p&gt;In der Theorie bedeutet 100 % Zeilenausführung: &quot;keine versteckten Bugs.&quot; In der Praxis:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Abnehmender Grenznutzen: 90 %→95 % verdoppelt oft deine Testsuite, bringt aber nur eine einstellige Risikoreduktion.&lt;/li&gt;
&lt;li&gt;Falsche Sicherheit: Ein Test, der eine Funktion ohne Assertion aufruft, &lt;strong&gt;zählt trotzdem&lt;/strong&gt; als abgedeckt.&lt;/li&gt;
&lt;li&gt;Business-Realität: Jeder zusätzliche Test ist Zeit, die &lt;strong&gt;nicht&lt;/strong&gt; in Features fließt, nach denen deine Kunden gefragt haben.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Die Aerospace-Leute können 100 % anpeilen — dort geht es um Leben und Tod. Für den Rest von uns ist &lt;strong&gt;~80 % die 80/20-Linie&lt;/strong&gt;. Dort landen die meisten Projekte nach ROI-Rechnungen. &lt;a href=&quot;https://www.testdevlab.com/blog/why-is-high-test-coverage-important&quot; target=&quot;_blank&quot;&gt;testdevlab.com&lt;/a&gt; nennt aus genau diesem Grund den Bereich 70–90 %.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;Die praktische Tabelle, die ich benutze&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Coverage&lt;/th&gt;
&lt;th&gt;Meine Übersetzung&lt;/th&gt;
&lt;th&gt;Handlung&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;100 %&lt;/td&gt;
&lt;td&gt;&quot;Wir sind eine Library, die Raketen fliegt&quot;&lt;/td&gt;
&lt;td&gt;Den Aufwand akzeptieren.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;90 % +&lt;/td&gt;
&lt;td&gt;&quot;Library, von der viel Geld abhängt&quot;&lt;/td&gt;
&lt;td&gt;Nur für Module mit hoher Priorität.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;80 %&lt;/td&gt;
&lt;td&gt;Shippen, beobachten, dann iterieren.&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;60–70 %&lt;/td&gt;
&lt;td&gt;Merge-Gate — PR fehlschlagen lassen, wenn neuer Code dich darunter zieht.&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;#x3C; 50 %&lt;/td&gt;
&lt;td&gt;Wochenende mit Tech Debt — zuerst auf kritische Pfade umschwenken.&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Ich habe diese Zahlen aus &lt;a href=&quot;https://www.atlassian.com/continuous-delivery/software-testing/code-coverage&quot; target=&quot;_blank&quot;&gt;Atlassians internem Leitfaden&lt;/a&gt; geklaut: 60 % &quot;acceptable&quot;, 75 % &quot;commendable&quot;, 90 % &quot;exemplary&quot;. Funktioniert in jeder Retro.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;Wie ich 80 % erreiche, ohne zu weinen (TypeScript-Playbook)&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;Jest + Istanbul direkt out of the box&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Coverage-Gate in CI&lt;/strong&gt;&lt;br&gt;
In &lt;code class=&quot;language-text&quot;&gt;jest.config.js&lt;/code&gt; füge ich hinzu:
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token literal-property property&quot;&gt;coverageThreshold&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;global&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;80&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token string-property property&quot;&gt;&apos;**/src/core/**&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;90&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;Ziel sind die heißen Nutzerpfade, nicht der Redux-Boilerplate-Logger.&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h2&gt;Wie ich 80 % in Laravel erreiche (PHP-Playbook)&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;PCOV für Geschwindigkeit in der Entwicklung installieren, Xdebug für Branch-Daten in CI.&lt;/li&gt;
&lt;li&gt;PHPUnit + diese Defaults in &lt;code class=&quot;language-text&quot;&gt;phpunit.xml&lt;/code&gt;:
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;xml&quot;&gt;&lt;pre class=&quot;language-xml&quot;&gt;&lt;code class=&quot;language-xml&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;filter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;whitelist&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;processUncoveredFiles&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;true&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;directory&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;suffix&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;.php&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;./src&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;directory&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;whitelist&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;filter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;Mutation Score &gt; Line Count mit &lt;a href=&quot;https://infection.github.io/&quot; target=&quot;_blank&quot;&gt;Infection&lt;/a&gt; — so finde ich Zeilen, die &quot;abgedeckt, aber nicht wirklich getestet&quot; sind.&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h2&gt;4 Regeln, nach denen mein Team lebt&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Neuer Code = Tests.&lt;/strong&gt; Diff Coverage ≥ 90 % vor dem Merge.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Erst refactoren, dann testen.&lt;/strong&gt; Untestbarer Code ist bereits Debt.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Den Build fehlschlagen lassen, nicht die Menschen.&lt;/strong&gt; Das Gate jedes Jahr um 5 % senken, statt Teams mit roten Dashboards zu brechen.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Bugs in Produktion messen&lt;/strong&gt; — wenn Coverage 85 % beträgt, aber Incidents hochgehen, ist &lt;strong&gt;Coverage&lt;/strong&gt; nicht der Schuldige; &lt;strong&gt;Assertions&lt;/strong&gt; sind es.&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h2&gt;TL;DR (auch für Executives und Recruiter)&lt;/h2&gt;
&lt;p&gt;Frag mich nicht nach einer &quot;magischen Zahl&quot;. Frag:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Welche Teile des Produkts dürfen nicht kaputtgehen?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Decke &lt;strong&gt;diese&lt;/strong&gt; mit 90 % ab. Gib dem Rest gesunde Smoke Tests. Benutze Code Coverage als &lt;strong&gt;Scheinwerfer&lt;/strong&gt;, nicht als Ziellinie, und vertraue den Bugs, die du &lt;strong&gt;fängst&lt;/strong&gt;, nicht den Zahlen, mit denen du &lt;strong&gt;prahlen&lt;/strong&gt; kannst.&lt;/p&gt;
&lt;p&gt;Lass das Coverage-Dashboard grün sein — deine Kunden werden es nie sehen, aber ihre Fehlerleiste bleibt leer.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;— Ende des Rants, zurück in den Editor.&lt;/em&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Mein essenzieller Leitfaden für wirksame Pull-Request-Reviews]]></title><description><![CDATA[Als jemand, der viel Code schreibt und reviewt, habe ich gelernt, dass Pull-Request-Reviews (PRs) mehr sind als Bugkontrollen. Es geht um…]]></description><link>https://bdteo.com/de/essential-guide-effective-pull-request-reviews/</link><guid isPermaLink="false">https://bdteo.com/de/essential-guide-effective-pull-request-reviews/</guid><pubDate>Sun, 06 Jul 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Als jemand, der viel Code schreibt und reviewt, habe ich gelernt, dass Pull-Request-Reviews (PRs) mehr sind als Bugkontrollen. Es geht um gemeinsame Verantwortung, Wissenstransfer und darum, zusammen besseren Code zu bauen. Hier ist ein knapper, praktischer Leitfaden, der PRs wertvoller und weniger schmerzhaft macht.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;1. Ziele eines guten Reviews&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Auf Verbesserung fokussieren, nicht auf Perfektion&lt;/strong&gt;&lt;br&gt;
Perfekter Code ist unrealistisch - ziele auf &lt;em&gt;besseren&lt;/em&gt; Code. Wenn ein PR Lesbarkeit, Wartbarkeit oder Korrektheit verbessert, approve ihn, auch wenn kleine Stilkorrekturen offen bleiben. Nutze &quot;Nit:&quot; für optionale Vorschläge.  &lt;a href=&quot;https://google.github.io/eng-practices/review/reviewer/standard.html?utm_source=chatgpt.com&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;google.github.io&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Gemeinsame Verantwortung &amp;#x26; Mentorship&lt;/strong&gt;&lt;br&gt;
Behandle PRs als gemeinsamen Code. Gib lehrreiches Feedback (&quot;Nit: du könntest hier X verwenden...&quot;), begleite Junior-Entwickler und bleib auch offen dafür, von ihnen zu lernen.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2&gt;2. Vorbereitung vor dem Review&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Autoren&lt;/strong&gt;: Self-Review: Tests, Linters und Formatters laufen lassen. Kontext in PR-Beschreibungen geben und komplexe Logik annotieren.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Reviewer&lt;/strong&gt;: Lies zuerst die Beschreibung. Verstehe das &quot;Warum&quot;, bevor du in den Code eintauchst.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2&gt;3. PRs klein &amp;#x26; fokussiert halten&lt;/h2&gt;
&lt;p&gt;Daten zeigen, dass die Review-Qualität jenseits von etwa ~400 LOC und ~60 Minuten deutlich abfällt.  &lt;a href=&quot;https://www.atlassian.com/blog/add-ons/code-review-best-practices?utm_source=chatgpt.com&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;atlassian.com&lt;/a&gt; &lt;a href=&quot;https://www.devzery.com/post/guide-to-best-code-review-practices?utm_source=chatgpt.com&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;devzery.com&lt;/a&gt; &lt;a href=&quot;https://mikeconley.ca/blog/2009/09/14/smart-bear-cisco-and-the-largest-study-on-code-review-ever/?utm_source=chatgpt.com&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;mikeconley.ca&lt;/a&gt;&lt;br&gt;
&lt;strong&gt;Richtlinien&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Bleib unter 200-400 LOC pro PR.  &lt;a href=&quot;https://mikeconley.ca/blog/2009/09/14/smart-bear-cisco-and-the-largest-study-on-code-review-ever/?utm_source=chatgpt.com&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;mikeconley.ca&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Halte Reviews unter 60 Minuten.&lt;/li&gt;
&lt;li&gt;Nutze für große Features gestapelte PRs (DB -&gt; API -&gt; UI).&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2&gt;4. Reviewer bewusst auswählen&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Ein primärer Reviewer&lt;/strong&gt;, idealerweise mit Domain-Wissen.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Maximal zwei Reviewer&lt;/strong&gt;, damit Verantwortung nicht diffundiert.  &lt;a href=&quot;https://support.smartbear.com/collaborator/docs/working-with/concepts/optimal-size.html?utm_source=chatgpt.com&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;support.smartbear.com&lt;/a&gt; &lt;a href=&quot;https://abseil.io/resources/swe-book/html/ch09.html?utm_source=chatgpt.com&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;abseil.io&lt;/a&gt; &lt;a href=&quot;https://slab.com/library/templates/google-code-review/?utm_source=chatgpt.com&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;slab.com&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Rotiere Reviewer für Cross-Training und einen gesunden Bus-Faktor.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2&gt;5. Was in einem PR zu prüfen ist&lt;/h2&gt;
&lt;p&gt;Nutze diese mentale Checkliste:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Korrektheit: Erfüllt es die Anforderungen und behandelt es Edge Cases?&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Design&lt;/strong&gt;: Ist es gut strukturiert und idiomatisch?&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Lesbarkeit&lt;/strong&gt;: Klare Namen, einfache Logik, konsistenter Stil.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Security&lt;/strong&gt;: Eingaben validieren, Ausgaben sanitizen, Leaks vermeiden.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Performance&lt;/strong&gt;: Auf schwere Schleifen und N+1-Queries achten.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Tests&lt;/strong&gt;: Abdeckung für Kern-, Rand- und Fehlerfälle.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Compliance&lt;/strong&gt;: Passende Dokumentation, CI, Lizenzen, Formatierung.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;So stellen wir sicher, dass wir Probleme früh erwischen - besonders Wartbarkeitsprobleme.  &lt;a href=&quot;https://google.github.io/eng-practices/review/reviewer/standard.html?utm_source=chatgpt.com&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;google.github.io&lt;/a&gt; &lt;a href=&quot;https://developers.google.com/blockly/guides/contribute/get-started/pr_review_process?utm_source=chatgpt.com&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;developers.google.com&lt;/a&gt; &lt;a href=&quot;https://google.github.io/eng-practices/review/?utm_source=chatgpt.com&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;google.github.io&lt;/a&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;6. Automatisierung nutzen&lt;/h2&gt;
&lt;p&gt;Lass Werkzeuge die Fleißarbeit erledigen:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Linters (ESLint, RuboCop, SonarQube)&lt;/li&gt;
&lt;li&gt;Formatters (Prettier, Black)&lt;/li&gt;
&lt;li&gt;CI-Pipelines mit Tests, Coverage und Security-Checks&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;So können menschliche Reviewer sich auf Logik, Architektur und Nuance konzentrieren.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;7. Konstruktives &amp;#x26; freundliches Feedback geben&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Sei respektvoll - kommentiere Vorschläge, nicht Menschen.&lt;/li&gt;
&lt;li&gt;Lobe, was gut gelungen ist.&lt;/li&gt;
&lt;li&gt;Sei handlungsorientiert: Erkläre das &lt;em&gt;Warum&lt;/em&gt; und schlage das &lt;em&gt;Wie&lt;/em&gt; vor.&lt;/li&gt;
&lt;li&gt;Präfixe Nicht-Blocker mit &quot;Nit:&quot; oder &quot;Optional:&quot;.  &lt;a href=&quot;https://www.atlassian.com/blog/loom/code-review-best-practices-2?utm_source=chatgpt.com&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;atlassian.com&lt;/a&gt; &lt;a href=&quot;https://google.github.io/eng-practices/review/reviewer/standard.html?utm_source=chatgpt.com&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;google.github.io&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Halte Diskussionen sachlich (&quot;wir&quot; &gt; &quot;du&quot;). Vermeide persönliche Kritik.&lt;/li&gt;
&lt;li&gt;Schlag ein synchrones Gespräch vor, wenn ein Hin und Her den Prozess blockiert.  &lt;a href=&quot;https://www.atlassian.com/blog/loom/code-review-best-practices-2?utm_source=chatgpt.com&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;atlassian.com&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2&gt;8. Den Prozess messen, nicht Menschen&lt;/h2&gt;
&lt;p&gt;Wichtige Metriken, um Trends zu verfolgen (nicht um Einzelpersonen zu bewerten):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Turnaround-Zeit&lt;/strong&gt; (PR offen -&gt; Merge)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Inspection Rate&lt;/strong&gt; (&amp;#x3C; 300-500 LOC/Std. ist am besten)  &lt;a href=&quot;https://www.atlassian.com/blog/loom/code-review-best-practices-2?utm_source=chatgpt.com&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;atlassian.com&lt;/a&gt; &lt;a href=&quot;https://developers.google.com/blockly/guides/contribute/get-started/pr_review_process?utm_source=chatgpt.com&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;developers.google.com&lt;/a&gt; &lt;a href=&quot;https://mikeconley.ca/blog/2009/09/14/smart-bear-cisco-and-the-largest-study-on-code-review-ever/?utm_source=chatgpt.com&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;mikeconley.ca&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Defektdichte&lt;/strong&gt; (Issues pro LOC)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Review-Abdeckung&lt;/strong&gt; über Komponenten hinweg&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Anzahl der Follow-up-Commits&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Nutze Erkenntnisse, um deinen Workflow zu verbessern - zum Beispiel kleinere PRs stärker zu betonen, Dokumentation zu verbessern oder bei schwierigen Modulen Wissen aufzubauen. Aber knüpfe Metriken niemals an Performance Reviews.  &lt;a href=&quot;https://mikeconley.ca/blog/2009/09/14/smart-bear-cisco-and-the-largest-study-on-code-review-ever/?utm_source=chatgpt.com&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;mikeconley.ca&lt;/a&gt; &lt;a href=&quot;https://google.github.io/eng-practices/review/reviewer/standard.html?utm_source=chatgpt.com&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;google.github.io&lt;/a&gt; &lt;a href=&quot;https://bssw.io/items/google-guidance-on-code-review?utm_source=chatgpt.com&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;bssw.io&lt;/a&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;9. Sprachspezifische Überlegungen&lt;/h2&gt;
&lt;p&gt;Verschiedene Paradigmen brauchen unterschiedliche Aufmerksamkeit:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;PHP/JavaScript/TS&lt;/strong&gt;: Async-Handling, XSS, SOLID-Prinzipien&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Python&lt;/strong&gt;: Ressourcenmanagement (&lt;code class=&quot;language-text&quot;&gt;with&lt;/code&gt;), PEP 8, Fallstricke bei Default-Args&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Haskell/Scala funktional&lt;/strong&gt;: Typsignaturen, Purity, Immutability, Macro-Checks&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;C/C++&lt;/strong&gt;: Memory Safety, Pointer, RAII&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Java&lt;/strong&gt;: Null-Safety, saubere Concurrency, SOLID&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Lisp&lt;/strong&gt;: Macro-Dokumentation, dynamische Typisierung, idiomatische Muster&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Passe Checklisten an deinen Stack an und beziehe Expertinnen und Experten ein, wenn Sprachen ungewohnt sind.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;Bonus: Empfohlene Deep-Dive-Quellen&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Googles &lt;em&gt;The Standard of Code Review&lt;/em&gt;&lt;/strong&gt; – Philosophie zu Code Health und Mentorship.  &lt;a href=&quot;https://google.github.io/eng-practices/review/reviewer/standard.html?utm_source=chatgpt.com&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;google.github.io&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Google Code Review Developer Guide&lt;/strong&gt; – Checklistenartige Orientierung.  &lt;a href=&quot;https://bssw.io/items/google-guidance-on-code-review?utm_source=chatgpt.com&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;bssw.io&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;SmartBear/Cisco-Studie&lt;/strong&gt; – Empirische Erkenntnisse zu PR-Größe und Timing.  &lt;a href=&quot;https://mikeconley.ca/blog/2009/09/14/smart-bear-cisco-and-the-largest-study-on-code-review-ever/?utm_source=chatgpt.com&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;mikeconley.ca&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Atlassian &quot;5 Code Review Best Practices&quot;&lt;/strong&gt; – Praktische Tipps zu Stil und Teamarbeit.  &lt;a href=&quot;https://www.atlassian.com/blog/add-ons/code-review-best-practices?utm_source=chatgpt.com&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;atlassian.com&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Blockly PR Flow&lt;/strong&gt; – Realer gestufter Review-Prozess.  &lt;a href=&quot;https://developers.google.com/blockly/guides/contribute/get-started/pr_review_process?utm_source=chatgpt.com&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;developers.google.com&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2&gt;Schlussgedanken&lt;/h2&gt;
&lt;p&gt;PR-Reviews, richtig gemacht, sind mehr als Quality Gates. Sie sind Motoren für Lernen, Zusammenarbeit und Engineering-Exzellenz. Wenn respektvolle Kultur, gutes Tooling, dateninformierte Prozesse und durchdachtes Feedback zusammenkommen, werden Code Reviews zu lohnenden Gesprächen - nicht zu lästigen Aufgaben.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Viel Freude beim Reviewen!&lt;/strong&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;Schreib gern einen Kommentar oder melde dich, wenn du tiefer einsteigen oder deine eigenen Review-Tipps teilen möchtest!&lt;/em&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Raucherkennung mit der Apple Watch: Still Mirror bauen (Swift, SWT)]]></title><description><![CDATA[Die Idee, unsere Gewohnheiten wirklich zu verstehen, besonders jene, die wir fast unbewusst ausführen, hat mich schon immer fasziniert. Was…]]></description><link>https://bdteo.com/de/apple-watch-still-mirror-swift-swt-passive-smoking-detection/</link><guid isPermaLink="false">https://bdteo.com/de/apple-watch-still-mirror-swift-swt-passive-smoking-detection/</guid><pubDate>Tue, 13 May 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Die Idee, unsere Gewohnheiten wirklich zu verstehen, besonders jene, die wir fast unbewusst ausführen, hat mich schon immer fasziniert. Was wäre, wenn unsere Wearables uns einen sanften, nicht wertenden Spiegel für diese Muster anbieten könnten? Aus dieser Frage entstand das Projekt &quot;Still Mirror&quot;: ein Versuch, Rauch- oder Vaping-Ereignisse passiv anhand der reichhaltigen physiologischen Daten einer Apple Watch zu erkennen, ohne manuelle Eingaben durch den Nutzer zu verlangen. Es geht nicht darum, noch eine weitere Entwöhnungs-App zu bauen, sondern um ein Werkzeug für reine, unverfälschte Wahrnehmung.&lt;/p&gt;
&lt;h2&gt;Die Herausforderung: Ein Flüstern in einer Symphonie aus Rauschen&lt;/h2&gt;
&lt;p&gt;Die zentrale Herausforderung ist enorm: Wie unterscheidet man die subtile physiologische Signatur eines Rauch-/Vaping-Ereignisses von den unzähligen anderen Alltagsaktivitäten und körperlichen Reaktionen? Stress, ein zügiger Spaziergang, ein erschreckendes Geräusch oder sogar eine Tasse Kaffee können vorübergehende Veränderungen der Herzfrequenz (HR) und der Herzfrequenzvariabilität (HRV) auslösen. Das Signal, nach dem wir suchen, ist oft ein Flüstern in einer Symphonie physiologischen Rauschens.&lt;/p&gt;
&lt;p&gt;Aber um diese flüchtigen Ereignisse wirklich zu isolieren, brauchte ich eine ausgefeiltere Technik zur Signalverarbeitung.&lt;/p&gt;
&lt;figure&gt;
  &lt;span class=&quot;gatsby-resp-image-wrapper&quot; style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;&gt;
      &lt;a class=&quot;gatsby-resp-image-link&quot; href=&quot;https://bdteo.com/static/6d861aafdcf69be2e1d02780744e708e/ac99c/apple-watch-swift-xcode.jpg&quot; style=&quot;display: block&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;
    &lt;span class=&quot;gatsby-resp-image-background-image&quot; style=&quot;padding-bottom: 66.45569620253164%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAANABQDASIAAhEBAxEB/8QAGAAAAwEBAAAAAAAAAAAAAAAAAAMFAgT/xAAWAQEBAQAAAAAAAAAAAAAAAAAAAQL/2gAMAwEAAhADEAAAAZyKfJm5GB//xAAaEAADAAMBAAAAAAAAAAAAAAABAgMAEBIT/9oACAEBAAEFAqVorgdDxyk1djLnX//EABYRAQEBAAAAAAAAAAAAAAAAAAASAf/aAAgBAwEBPwGdS//EABYRAQEBAAAAAAAAAAAAAAAAAAASAf/aAAgBAgEBPwGsU//EAB0QAAIBBAMAAAAAAAAAAAAAAAABAhAREiExMkH/2gAIAQEABj8Cks3yOTZ2Rf006f/EABoQAAMBAAMAAAAAAAAAAAAAAAABESExUcH/2gAIAQEAAT8hR2CEEVu0oXxlOyJvEVS5p//aAAwDAQACAAMAAAAQo8//xAAWEQEBAQAAAAAAAAAAAAAAAAAAARH/2gAIAQMBAT8Qqw//xAAVEQEBAAAAAAAAAAAAAAAAAAABEP/aAAgBAgEBPxAaP//EABsQAQADAQEBAQAAAAAAAAAAAAEAESExQVGh/9oACAEBAAE/ELGVBt5eS9mCAa37MhK0v2Xizqdhapffr9jINh6z/9k=&apos;); background-size: cover; display: block;&quot;&gt;&lt;/span&gt;
  &lt;img class=&quot;gatsby-resp-image-image&quot; alt=&quot;Entwicklerarbeitsplatz mit Xcode, der Swift-Code für eine Apple-Watch-App zeigt, mit HealthKit-Diagrammen im Hintergrund.&quot; title=&quot;&quot; src=&quot;https://bdteo.com/static/6d861aafdcf69be2e1d02780744e708e/828fb/apple-watch-swift-xcode.jpg&quot; srcset=&quot;https://bdteo.com/static/6d861aafdcf69be2e1d02780744e708e/ff44c/apple-watch-swift-xcode.jpg 158w,
https://bdteo.com/static/6d861aafdcf69be2e1d02780744e708e/a6688/apple-watch-swift-xcode.jpg 315w,
https://bdteo.com/static/6d861aafdcf69be2e1d02780744e708e/828fb/apple-watch-swift-xcode.jpg 630w,
https://bdteo.com/static/6d861aafdcf69be2e1d02780744e708e/0ede0/apple-watch-swift-xcode.jpg 945w,
https://bdteo.com/static/6d861aafdcf69be2e1d02780744e708e/3ac88/apple-watch-swift-xcode.jpg 1260w,
https://bdteo.com/static/6d861aafdcf69be2e1d02780744e708e/ac99c/apple-watch-swift-xcode.jpg 1536w&quot; sizes=&quot;(max-width: 630px) 100vw, 630px&quot; style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;&gt;
  &lt;/a&gt;
    &lt;/span&gt;
  &lt;figcaption&gt;Abb. 1. – Das Apple-Entwicklungsökosystem: Xcode, Swift und HealthKit sind zentral, um &apos;Still Mirror&apos; auf der Apple Watch zum Leben zu erwecken.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h2&gt;Wahl des Werkzeugkastens: Apple-Ökosystem &amp;#x26; Swift&lt;/h2&gt;
&lt;p&gt;Für ein Projekt, das auf die Apple Watch zielt, ist die Ökosystemwahl klar:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Xcode und Swift:&lt;/strong&gt; Die native Entwicklungsumgebung für Apple-Plattformen. Sich darauf einzulassen bedeutete, tiefer in Swift einzutauchen, eine Sprache, die ich elegant und mächtig finde, und sich durch die Feinheiten von Xcode zu bewegen.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;HealthKit:&lt;/strong&gt; Apples Framework ist das Tor zu den wesentlichen Datenströmen: Herzfrequenz, HRV (SDNN/RMSSD), SpO2 (besonders relevant für Verbrennung vs. Vaping) und Aktivitätsniveau. Das datenschutzorientierte Design von HealthKit ist entscheidend für eine App, die mit so sensiblen Daten umgeht.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;watchOS-Einschränkungen:&lt;/strong&gt; Für die Watch zu entwickeln bedeutet, Funktionalität ständig gegen Ressourcenbeschränkungen abzuwägen – Akkulaufzeit und Hintergrundverarbeitung sind immer präsent.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Das algorithmische Herz: Stationary Wavelet Transform (SWT)&lt;/h2&gt;
&lt;p&gt;Klassische Zeitreihenanalyse hat oft Schwierigkeiten mit nicht-stationären Signalen – Signalen, deren statistische Eigenschaften (wie Mittelwert und Varianz) sich über die Zeit verändern. Physiologische Daten sind notorisch nicht-stationär. Genau hier kommt die &lt;strong&gt;Stationary Wavelet Transform (SWT)&lt;/strong&gt; ins Spiel.&lt;/p&gt;
&lt;p&gt;Anders als die übliche Discrete Wavelet Transform (DWT), die verschiebungsvariant ist (das heißt, eine kleine Verschiebung im Eingangssignal kann die Wavelet-Koeffizienten dramatisch verändern), ist SWT verschiebungsinvariant. Das macht sie robuster für die Analyse von Signalen, bei denen der exakte Zeitpunkt von Ereignissen entscheidend ist, aber leicht variieren kann.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Warum SWT für dieses Problem?&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Zeit-Frequenz-Lokalisierung:&lt;/strong&gt; SWT kann ein Signal in verschiedene Frequenzbänder zerlegen und dabei zeitliche Information bewahren. Das heißt, wir können nach bestimmten Frequenzmerkmalen suchen (z. B. plötzliche Ausbrüche hochfrequenter Aktivität in der HR oder bestimmte Veränderungen in HRV-Frequenzbändern), die zu präzisen Momenten auftreten.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Entrauschen:&lt;/strong&gt; Physiologische Signale sind verrauscht. SWT kann helfen, das zugrunde liegende &quot;wahre&quot; Signal vom Zufallsrauschen zu trennen, indem Wavelet-Koeffizienten auf verschiedenen Skalen analysiert werden.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Erkennung transienter Ereignisse:&lt;/strong&gt; Sie ist besonders gut darin, abrupte Änderungen, Spitzen oder kurzlebige Ereignisse in einem Signal zu identifizieren, also genau das, was wir von der akuten physiologischen Reaktion auf Nikotinzufuhr erwarten könnten.&lt;/li&gt;
&lt;/ol&gt;
&lt;figure&gt;
  &lt;span class=&quot;gatsby-resp-image-wrapper&quot; style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;&gt;
      &lt;a class=&quot;gatsby-resp-image-link&quot; href=&quot;https://bdteo.com/static/b2719b44c36d6548b24505286c03c80f/ac99c/stationary-wavelet-transform-visualization.jpg&quot; style=&quot;display: block&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;
    &lt;span class=&quot;gatsby-resp-image-background-image&quot; style=&quot;padding-bottom: 66.45569620253164%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAANABQDASIAAhEBAxEB/8QAFwABAQEBAAAAAAAAAAAAAAAAAAECBf/EABYBAQEBAAAAAAAAAAAAAAAAAAIBA//aAAwDAQACEAMQAAAB5LFeMRF//8QAFBABAAAAAAAAAAAAAAAAAAAAIP/aAAgBAQABBQJf/8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPwE//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPwE//8QAFRABAQAAAAAAAAAAAAAAAAAAIEH/2gAIAQEABj8Ci//EABoQAQEAAgMAAAAAAAAAAAAAAAEAEGERITH/2gAIAQEAAT8hHUXRL34XMuP/2gAMAwEAAgADAAAAEGQv/8QAFREBAQAAAAAAAAAAAAAAAAAAEBH/2gAIAQMBAT8Qh//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8QP//EABwQAAMAAQUAAAAAAAAAAAAAAAABESFBcaGx8P/aAAgBAQABPxBKS7xcyanUtheUppyN5P/Z&apos;); background-size: cover; display: block;&quot;&gt;&lt;/span&gt;
  &lt;img class=&quot;gatsby-resp-image-image&quot; alt=&quot;Abstrakte Visualisierung eines physiologischen Signals, das durch die Stationary Wavelet Transform in mehrere Frequenzbänder zerlegt wird.&quot; title=&quot;&quot; src=&quot;https://bdteo.com/static/b2719b44c36d6548b24505286c03c80f/828fb/stationary-wavelet-transform-visualization.jpg&quot; srcset=&quot;https://bdteo.com/static/b2719b44c36d6548b24505286c03c80f/ff44c/stationary-wavelet-transform-visualization.jpg 158w,
https://bdteo.com/static/b2719b44c36d6548b24505286c03c80f/a6688/stationary-wavelet-transform-visualization.jpg 315w,
https://bdteo.com/static/b2719b44c36d6548b24505286c03c80f/828fb/stationary-wavelet-transform-visualization.jpg 630w,
https://bdteo.com/static/b2719b44c36d6548b24505286c03c80f/0ede0/stationary-wavelet-transform-visualization.jpg 945w,
https://bdteo.com/static/b2719b44c36d6548b24505286c03c80f/3ac88/stationary-wavelet-transform-visualization.jpg 1260w,
https://bdteo.com/static/b2719b44c36d6548b24505286c03c80f/ac99c/stationary-wavelet-transform-visualization.jpg 1536w&quot; sizes=&quot;(max-width: 630px) 100vw, 630px&quot; style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;&gt;
  &lt;/a&gt;
    &lt;/span&gt;
  &lt;figcaption&gt;Abb. 2. – Visualisierung der Stationary Wavelet Transform, die ein Signal über die Zeit in seine Frequenzbestandteile zerlegt und so die Mustererkennung unterstützt.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Im Kern wirkt SWT wie ein ausgefeilter Satz von Filtern, der uns erlaubt, Muster in HR-, HRV- und potenziell SpO2-Daten zu &quot;sehen&quot;, die durch Rauschen oder langfristige Trends verdeckt sein könnten. Wir können nach charakteristischen &quot;Formen&quot; oder Energieänderungen in bestimmten Wavelet-Unterbändern suchen, die dem physiologischen Ruck entsprechen.&lt;/p&gt;
&lt;h2&gt;Die Entwicklungsreise: Von Daten zur Erkennung&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Datenerfassung (HealthKit):&lt;/strong&gt; Zuverlässiges Abrufen von HealthKit-Daten im Hintergrund einrichten, Nutzerberechtigungen respektieren und Datenaktualisierungen effizient verarbeiten.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Signalvorverarbeitung:&lt;/strong&gt; Die eingehenden HR-, HRV- und SpO2-Daten bereinigen. Dazu gehört der Umgang mit fehlenden Datenpunkten und vielleicht eine erste Filterung, bevor SWT angewendet wird.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;SWT-Anwendung:&lt;/strong&gt; Die Stationary Wavelet Transform auf Segmente der physiologischen Zeitreihen anwenden. Dazu gehört die Wahl eines geeigneten Mother Wavelets (z. B. Daubechies, Symlet) und einer Zerlegungsebene.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Feature-Extraktion aus Wavelet-Koeffizienten:&lt;/strong&gt; Hier passiert die Magie (und eine Menge Experimentieren). Statt direkt auf rohe HR-/HRV-Werte zu schauen, analysieren wir die SWT-Koeffizienten. Relevante Features könnten sein:
&lt;ul&gt;
&lt;li&gt;Energie in bestimmten Detailkoeffizienten-Bändern rund um den Zeitpunkt eines vermuteten Ereignisses.&lt;/li&gt;
&lt;li&gt;Statistische Eigenschaften (Varianz, Kurtosis) der Koeffizienten.&lt;/li&gt;
&lt;li&gt;Kreuzkorrelation zwischen Wavelet-Koeffizienten verschiedener physiologischer Signale (z. B. HR und HRV).&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Erkennungslogik/-modell:&lt;/strong&gt; Anfangs könnte das ein regelbasiertes System sein, das nach bestimmten Mustern in den extrahierten Wavelet-Features sucht (z. B. &quot;ein signifikanter Energieanstieg in HR-Detailkoeffizienten auf Skala X, zeitgleich mit einem starken Energieabfall in HRV-Detailkoeffizienten auf Skala Y, während einer Phase geringer körperlicher Aktivität&quot;). Später könnte sich daraus ein Machine-Learning-Modell entwickeln, das auf diesen Features trainiert wird.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Konfidenzbewertung:&lt;/strong&gt; Wie in meinem MVPS-Algorithmus skizziert, ist es entscheidend, für jedes erkannte Ereignis einen Konfidenzwert zu erzeugen, der Stärke und Klarheit der Signatur widerspiegelt.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;watchOS-App-Implementierung:&lt;/strong&gt; Den Kernalgorithmus zur Erkennung auf der Apple Watch ausführen und dabei auf Akkulaufzeit optimieren (z. B. Daten in Batches verarbeiten, Analyse intelligent auslösen).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;iOS-Companion-App:&lt;/strong&gt; Zur Anzeige der Timeline erkannter Ereignisse, für Einblicke und zur Verwaltung von Einstellungen. Datensynchronisierung via WatchConnectivity ist hier zentral.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;Gesundheit &amp;#x26; Ethik: Die &quot;Spiegel&quot;-Philosophie&lt;/h2&gt;
&lt;p&gt;Es ist wichtig, noch einmal zu betonen, dass &quot;Still Mirror&quot; als &lt;em&gt;Awareness-Werkzeug&lt;/em&gt; gedacht ist, nicht als Medizinprodukt oder Entwöhnungsprogramm.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Privacy First:&lt;/strong&gt; Die gesamte Verarbeitung, besonders die sensible Algorithmusarbeit, sollte idealerweise auf dem Gerät stattfinden. Der Zugriff auf HealthKit-Daten ist strikt berechtigungsbasiert.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Kein Urteil:&lt;/strong&gt; Die Oberfläche der App und alle Einsichten, die sie liefert, müssen neutral bleiben und Muster lediglich widerspiegeln, ohne vorschreibende Ratschläge oder Beschämung.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Genauigkeit &amp;#x26; Transparenz:&lt;/strong&gt; Nutzer müssen die Grenzen der App verstehen. Falsch positive und falsch negative Ergebnisse sind bei einer so komplexen, passiven Erkennung unvermeidlich. Transparenz über die Konfidenz von Erkennungen ist wichtig.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Selbstermächtigung des Nutzers:&lt;/strong&gt; Ziel ist es, Nutzern Daten über ihren eigenen Körper und ihre Gewohnheiten zu geben, damit sie ihre eigenen informierten Entscheidungen treffen können.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Swift lernen und sich im Apple-Ökosystem zurechtfinden&lt;/h2&gt;
&lt;p&gt;Für Entwickler, die vor allem aus anderen Welten kommen (wie meinen PHP-/Laravel-Wurzeln), ist der Einstieg in Swift, SwiftUI, Xcode und die spezifischen Einschränkungen der watchOS-Entwicklung eine deutliche Lernkurve. Apples Frameworks haben eine eigene Philosophie. App-Lebenszyklen, Hintergrundaufgaben, HealthKit-Queries und Kommunikation zwischen Geräten (WatchConnectivity) haben jeweils ihre eigenen Muster und &quot;Apple-Wege&quot;, Dinge zu tun. Trotzdem machen die reichhaltige Dokumentation, die starke Community und die Kraft von Swift diese Reise lohnend.&lt;/p&gt;
&lt;h2&gt;Fazit: Das Potenzial eines stillen Beobachters&lt;/h2&gt;
&lt;p&gt;&quot;Still Mirror&quot; ist weiterhin eine Erkundung, ein anspruchsvolles Unterfangen, um die Grenzen dessen zu verschieben, was passives Sensing auf einem Consumer-Wearable leisten kann. Die Stationary Wavelet Transform bietet einen vielversprechenden Weg, komplexe physiologische Signale zu zerlegen und die subtilen Signaturen freizulegen, nach denen wir suchen.&lt;/p&gt;
&lt;p&gt;Die Reise besteht nicht nur daraus, Swift-Code zu schreiben und mit Xcode zu ringen, sondern auch daraus, in Signalverarbeitungstheorie einzutauchen, menschliche Physiologie zu verstehen und die ethischen Folgen einer solchen Technologie sorgfältig abzuwägen. Ob &quot;Still Mirror&quot; einmal eine breit genutzte App wird oder eine filigrane technische Erkundung bleibt: Der Prozess selbst zeigt diese faszinierende Schnittstelle von KI, Gesundheit und persönlicher Technologie. Es geht darum, diese ruhige, reflektierende Oberfläche zu bauen – einen stillen Spiegel – für mehr Selbstwahrnehmung.&lt;/p&gt;
&lt;p&gt;Was denkst du über den Einsatz fortgeschrittener Signalverarbeitung wie SWT zur passiven Erkennung von Gewohnheiten? Ich würde deine Gedanken dazu gern unten in den Kommentaren lesen!&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Android-Emulator-Bluetooth auf dem M1-Mac mit Bumble und API 32 reparieren]]></title><description><![CDATA[Wenn du als Entwickler auf einem M1/M2/M3-Mac mit Bluetooth arbeitest und versuchst, die Bluetooth-Funkeinheit deines Host-Rechners im…]]></description><link>https://bdteo.com/de/m1-mac-android-emulator-bluetooth-passthrough-bumble/</link><guid isPermaLink="false">https://bdteo.com/de/m1-mac-android-emulator-bluetooth-passthrough-bumble/</guid><pubDate>Mon, 14 Apr 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Wenn du als Entwickler auf einem M1/M2/M3-Mac mit Bluetooth arbeitest und versuchst, die Bluetooth-Funkeinheit deines Host-Rechners im Android Emulator nutzbar zu machen, hast du vermutlich schon etwas gelitten. Was eigentlich unkompliziert wirken &lt;em&gt;sollte&lt;/em&gt;, wird oft zu einem frustrierenden Kaninchenbau aus fehlgeschlagenen Verbindungen, kryptischen Fehlern und Dokumentations-Sackgassen. Ich bin genau durch diesen Kampf gegangen, und nach mehreren Mauern habe ich endlich eine Kombination mit dem Python-Bluetooth-Stack &lt;strong&gt;Bumble&lt;/strong&gt; gefunden, die &lt;em&gt;tatsächlich funktioniert&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Das ist nicht noch eine theoretische Anleitung; es ist der Schritt-für-Schritt-Bericht darüber, was gescheitert ist und, wichtiger, was &lt;em&gt;funktioniert&lt;/em&gt; hat, um das Bluetooth meines M1 Mac Pro (in meinem Fall über einen externen USB-Dongle, auch wenn das Prinzip vielleicht auch für interne Funkmodule gilt) mit einem Android-12L-Emulator (API 32) zu verbinden.&lt;/p&gt;
&lt;h2&gt;Das Ziel: Echtes Bluetooth im Emulator&lt;/h2&gt;
&lt;p&gt;Das Ziel war simpel: Der Android Emulator sollte den physischen Bluetooth-Controller meines Macs verwenden, statt seines eigenen eingeschränkten virtuellen Controllers. Das ist entscheidend, wenn man Apps testet, die mit echten Bluetooth-Geräten interagieren.&lt;/p&gt;
&lt;h2&gt;Das Werkzeug: Bumble betritt die Bühne&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/google/bumble&quot;&gt;Bumble&lt;/a&gt; ist ein mächtiger Python-Bluetooth-Stack. Das zentrale Werkzeug für diese Aufgabe ist &lt;code class=&quot;language-text&quot;&gt;bumble-hci-bridge&lt;/code&gt;, das sich auf der einen Seite mit einem physischen HCI (Host Controller Interface) verbinden und es auf der anderen Seite über verschiedene Transportschichten (wie TCP oder gRPC) bereitstellen kann.&lt;/p&gt;
&lt;h2&gt;Versuch #1: Die QEMU-Socket-Methode (der logische erste Versuch)&lt;/h2&gt;
&lt;p&gt;Ausgehend von allgemeinem QEMU-Wissen und ein paar älteren Anleitungen bestand der erste Ansatz darin, Emulator-Flags zu verwenden, um einen virtuellen seriellen Port (hinterlegt durch einen TCP-Socket) direkt mit der HCI-Bridge zu verbinden.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Bridge starten (TCP-Server-Modus):&lt;/strong&gt; Wir verbanden Bumble mit dem physischen Dongle (der auf meiner Maschine überraschenderweise mit &lt;code class=&quot;language-text&quot;&gt;usb:0&lt;/code&gt; besser funktionierte als mit seiner konkreten VID:PID &lt;code class=&quot;language-text&quot;&gt;usb:0b05:17cb&lt;/code&gt; - M1-Eigenheiten!) und ließen es auf einem TCP-Port lauschen.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# In Terminal 1:&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; python3 &lt;span class=&quot;token parameter variable&quot;&gt;-m&lt;/span&gt; bumble.apps.hci_bridge usb:0 tcp-server:0.0.0.0:6789
&lt;span class=&quot;token comment&quot;&gt;# Output showed &apos;&gt;&gt;&gt; connected&apos; twice - success connecting to USB and starting TCP server.&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Emulator mit QEMU-Flags starten:&lt;/strong&gt; Wir passten das Emulator-Startskript an (anfangs mit Ziel API 34), um &lt;code class=&quot;language-text&quot;&gt;-qemu&lt;/code&gt;-Flags hinzuzufügen, die einen virtuellen seriellen Port (&lt;code class=&quot;language-text&quot;&gt;virtserialport&lt;/code&gt;) auf ein Zeichengerät (&lt;code class=&quot;language-text&quot;&gt;chardev&lt;/code&gt;) leiteten, das von einem TCP-Socket zur Bridge hinterlegt war.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# Snippet from launch script:&lt;/span&gt;
emulator &lt;span class=&quot;token parameter variable&quot;&gt;-avd&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;avd_name&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;..&lt;/span&gt;. &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;token parameter variable&quot;&gt;-qemu&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;token parameter variable&quot;&gt;-device&lt;/span&gt; virtio-serial-device &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;token parameter variable&quot;&gt;-device&lt;/span&gt; virtserialport,chardev&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;bt,name&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;bt &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;token parameter variable&quot;&gt;-chardev&lt;/span&gt; socket,id&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;bt,host&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;localhost,port&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;6789&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;..&lt;/span&gt;.&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Das Ergebnis? Teilerfolg, am Ende Fehlschlag:&lt;/strong&gt; Mit &lt;code class=&quot;language-text&quot;&gt;lsof&lt;/code&gt; konnten wir sehen, dass der QEMU-Prozess des Emulators &lt;em&gt;tatsächlich&lt;/em&gt; eine TCP-Verbindung zur Bumble-Bridge aufbaute. Allerdings schickte der Android-Bluetooth-Stack &lt;em&gt;im Inneren&lt;/em&gt; des Emulators nie echte HCI-Kommandos darüber. Bluetooth in den Android-Einstellungen umzuschalten bewirkte nichts. Die Bridge-Logs blieben nach der initialen Verbindung stumm. &lt;strong&gt;Sackgasse.&lt;/strong&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;Versuch #2: Die Standard-Netsim-Bridge (nach Bumble-Dokumentation)&lt;/h2&gt;
&lt;p&gt;Die Bumble-Dokumentation erwähnt eine Bridge zur &quot;Netsim&quot;-gRPC-Schnittstelle des Emulators. Netsim (und sein Kern, Root Canal) ist das neuere virtuelle Bluetooth-Controller-System des Emulators.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Bridge starten (Netsim-Controller-Modus):&lt;/strong&gt; Wir konfigurierten die Bridge so, dass sie als Netsim-Controller arbeitet, auf dem standardmäßigen gRPC-Port (8554) lauscht und sich mit dem physischen Dongle verbindet.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# In Terminal 1:&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; python3 &lt;span class=&quot;token parameter variable&quot;&gt;-m&lt;/span&gt; bumble.apps.hci_bridge android-netsim:_:8554,mode&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;controller usb:0
&lt;span class=&quot;token comment&quot;&gt;# Output showed &apos;&gt;&gt;&gt; connected&apos; twice - success connecting to USB and starting Netsim gRPC server.&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Emulator starten (Standard-Backend):&lt;/strong&gt; Wir setzten das Startskript zurück (immer noch mit API 34), entfernten die &lt;code class=&quot;language-text&quot;&gt;-qemu&lt;/code&gt;-Flags und fügten &lt;code class=&quot;language-text&quot;&gt;-packet-streamer-endpoint default&lt;/code&gt; hinzu, damit er sicher das Netsim-Backend zu verwenden versucht.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# Snippet from launch script:&lt;/span&gt;
emulator &lt;span class=&quot;token parameter variable&quot;&gt;-avd&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;avd_name&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;..&lt;/span&gt;. &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
    -packet-streamer-endpoint default &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;..&lt;/span&gt;.&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Das Ergebnis? Keine Verbindung:&lt;/strong&gt; Diesmal startete der Emulator, aber die Bumble-Bridge zeigte keinerlei Anzeichen einer eingehenden gRPC-Verbindung vom Emulator. Die Emulator-Logs zeigten keine offensichtlichen Verbindungsfehler, aber Bluetooth blieb unbrauchbar. &lt;strong&gt;Noch eine Sackgasse.&lt;/strong&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;Versuch #3: API-Downgrade + expliziter Netsim-Endpunkt (der Gewinner!)&lt;/h2&gt;
&lt;figure&gt;
  &lt;span class=&quot;gatsby-resp-image-wrapper&quot; style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;&gt;
      &lt;a class=&quot;gatsby-resp-image-link&quot; href=&quot;https://bdteo.com/static/cd1aa56ebd8947295c8b2ab1adab5b06/ac99c/featured-2.jpg&quot; style=&quot;display: block&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;
    &lt;span class=&quot;gatsby-resp-image-background-image&quot; style=&quot;padding-bottom: 66.45569620253164%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAANABQDASIAAhEBAxEB/8QAFwAAAwEAAAAAAAAAAAAAAAAAAAQFAv/EABUBAQEAAAAAAAAAAAAAAAAAAAED/9oADAMBAAIQAxAAAAHDUViFaRAE/8QAGhABAAIDAQAAAAAAAAAAAAAAAgEDAAQREv/aAAgBAQABBQIvt+xPisWCQJ5LtlDP/8QAFhEBAQEAAAAAAAAAAAAAAAAAABJB/9oACAEDAQE/AcU//8QAFxEAAwEAAAAAAAAAAAAAAAAAAAESIf/aAAgBAgEBPwFrSD//xAAbEAADAAIDAAAAAAAAAAAAAAAAARECIhASIf/aAAgBAQAGPwJR6mvgu2SpSRcf/8QAGhABAAIDAQAAAAAAAAAAAAAAAQAhEBFBUf/aAAgBAQABPyEyA8RwUWv2PBtb0xCHI4dncf/aAAwDAQACAAMAAAAQHz//xAAWEQADAAAAAAAAAAAAAAAAAAABEBH/2gAIAQMBAT8QER//xAAYEQACAwAAAAAAAAAAAAAAAAAAEQExUf/aAAgBAgEBPxCRVQun/8QAGxABAAMBAQEBAAAAAAAAAAAAAQARITFBYfD/2gAIAQEAAT8QdIcFUc997FbSRXx8nEVD8bKk7V0lkDiK2DcYuuE//9k=&apos;); background-size: cover; display: block;&quot;&gt;&lt;/span&gt;
  &lt;img class=&quot;gatsby-resp-image-image&quot; alt=&quot;Symbolische Brücke zwischen Apple- und Android-Plattformen&quot; title=&quot;&quot; src=&quot;https://bdteo.com/static/cd1aa56ebd8947295c8b2ab1adab5b06/828fb/featured-2.jpg&quot; srcset=&quot;https://bdteo.com/static/cd1aa56ebd8947295c8b2ab1adab5b06/ff44c/featured-2.jpg 158w,
https://bdteo.com/static/cd1aa56ebd8947295c8b2ab1adab5b06/a6688/featured-2.jpg 315w,
https://bdteo.com/static/cd1aa56ebd8947295c8b2ab1adab5b06/828fb/featured-2.jpg 630w,
https://bdteo.com/static/cd1aa56ebd8947295c8b2ab1adab5b06/0ede0/featured-2.jpg 945w,
https://bdteo.com/static/cd1aa56ebd8947295c8b2ab1adab5b06/3ac88/featured-2.jpg 1260w,
https://bdteo.com/static/cd1aa56ebd8947295c8b2ab1adab5b06/ac99c/featured-2.jpg 1536w&quot; sizes=&quot;(max-width: 630px) 100vw, 630px&quot; style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;&gt;
  &lt;/a&gt;
    &lt;/span&gt;
  &lt;figcaption&gt;
    Fig1. - Eine surreale Landschaft, in der gescheiterte Netzwerkkabel zwischen Apple- und Android-Felsformationen hängen, während eine einzelne Bumble-markierte Seilbrücke die beiden erfolgreich verbindet und leuchtende Datenpakete hinüberlaufen lässt.
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Websuchen zeigten allgemeine Berichte über Instabilität bei Bluetooth auf API-33/34-Emulatoren und mögliche Probleme damit, wie der Emulator das Netsim-Backend entdeckt oder sich damit verbindet, besonders wenn ein externes Werkzeug versucht, es abzufangen. Der Schlüssel schien zu sein, dem Emulator &lt;strong&gt;explizit zu sagen, wo der Netsim-gRPC-Server liegt&lt;/strong&gt;, und &lt;strong&gt;eine ältere API-Version zu versuchen&lt;/strong&gt;.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Bridge starten (Netsim-Controller-Modus, expliziter Port, &lt;code class=&quot;language-text&quot;&gt;usb:0&lt;/code&gt;):&lt;/strong&gt; Wie in Versuch #2, mit der Sicherstellung, dass sie auf einem bekannten Port (&lt;code class=&quot;language-text&quot;&gt;8554&lt;/code&gt;) lauscht und sich über den Index (&lt;code class=&quot;language-text&quot;&gt;usb:0&lt;/code&gt;), der zuverlässig funktionierte, mit dem physischen Dongle verbindet.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# In Terminal 1: (Keep this running!)&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; python3 &lt;span class=&quot;token parameter variable&quot;&gt;-m&lt;/span&gt; bumble.apps.hci_bridge android-netsim:_:8554,mode&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;controller usb:0&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Emulator anpassen und starten (API 32, expliziter Endpunkt):&lt;/strong&gt; Wir erstellten ein &lt;strong&gt;API-32-AVD (Android 12L)&lt;/strong&gt; mit Google Play Services (&lt;code class=&quot;language-text&quot;&gt;gplay_32_arm&lt;/code&gt;). Wir passten das Startskript an, damit es dieses AVD verwendet, und, entscheidend, änderten das Flag &lt;code class=&quot;language-text&quot;&gt;-packet-streamer-endpoint&lt;/code&gt; von &lt;code class=&quot;language-text&quot;&gt;default&lt;/code&gt; zur &lt;em&gt;exakten&lt;/em&gt; Adresse unserer Bridge.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# Snippet from the *successful* launch script (see full script below):&lt;/span&gt;
&lt;span class=&quot;token assign-left variable&quot;&gt;API_LEVEL&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;32&quot;&lt;/span&gt;
&lt;span class=&quot;token assign-left variable&quot;&gt;AVD_NAME&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;gplay_&lt;span class=&quot;token variable&quot;&gt;${API_LEVEL}&lt;/span&gt;_arm&quot;&lt;/span&gt;
&lt;span class=&quot;token assign-left variable&quot;&gt;SYSTEM_IMAGE_PKG&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;system-images;android-&lt;span class=&quot;token variable&quot;&gt;${API_LEVEL}&lt;/span&gt;;&lt;span class=&quot;token variable&quot;&gt;${IMAGE_TAG}&lt;/span&gt;;&lt;span class=&quot;token variable&quot;&gt;${ARCH}&lt;/span&gt;&quot;&lt;/span&gt;
&lt;span class=&quot;token assign-left variable&quot;&gt;BUMBLE_NETSIM_GRPC_PORT&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;8554&quot;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;..&lt;/span&gt;.
emulator &lt;span class=&quot;token parameter variable&quot;&gt;-avd&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$AVD_NAME&lt;/span&gt;&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;..&lt;/span&gt;. &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
    -packet-streamer-endpoint &lt;span class=&quot;token string&quot;&gt;&quot;localhost:&lt;span class=&quot;token variable&quot;&gt;$BUMBLE_NETSIM_GRPC_PORT&lt;/span&gt;&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;..&lt;/span&gt;.&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Das Ergebnis? Erfolg!&lt;/strong&gt; Diesmal funktionierte es!&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Das &lt;code class=&quot;language-text&quot;&gt;bumble-hci-bridge&lt;/code&gt;-Terminal begann kurz nach dem Start, gRPC-Verbindungslogs vom Emulator anzuzeigen.&lt;/li&gt;
&lt;li&gt;Sobald der Emulator gebootet war, führte das Einschalten von Bluetooth in den Android-Einstellungen zu einer Flut von HCI-Kommandos (Reset, Read Version, Set Event Mask usw.) im Bridge-Terminal.&lt;/li&gt;
&lt;li&gt;Die Suche nach Geräten innerhalb des Emulators nutzte erfolgreich die physische Bluetooth-Funkeinheit des Macs über den ASUS-Dongle!&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;Das funktionierende Rezept: Schritt für Schritt&lt;/h2&gt;
&lt;p&gt;Hier ist das genaue Vorgehen, das auf meinem M1 Mac Pro mit einem externen ASUS-USB-BT500-Dongle funktioniert hat:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Bumble installieren:&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;python3 &lt;span class=&quot;token parameter variable&quot;&gt;-m&lt;/span&gt; pip &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; bumble
&lt;span class=&quot;token comment&quot;&gt;# Potentially install libusb if needed: brew install libusb&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;(Optional, aber empfohlen) Natives macOS-USB-Bluetooth-Handling deaktivieren:&lt;/strong&gt; Einmal ausführen und &lt;strong&gt;neu starten&lt;/strong&gt;.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; nvram &lt;span class=&quot;token assign-left variable&quot;&gt;bluetoothHostControllerSwitchBehavior&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;never&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Bumble-Netsim-Bridge starten:&lt;/strong&gt; Öffne ein Terminal und führe aus (laufen lassen):&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; python3 &lt;span class=&quot;token parameter variable&quot;&gt;-m&lt;/span&gt; bumble.apps.hci_bridge android-netsim:_:8554,mode&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;controller usb:0&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;em&gt;(Prüfen, dass zweimal &lt;code class=&quot;language-text&quot;&gt;&gt;&gt;&gt; connected&lt;/code&gt; angezeigt wird.)&lt;/em&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Emulator-Startskript vorbereiten:&lt;/strong&gt; Speichere das unten angegebene &lt;em&gt;vollständige Skript&lt;/em&gt; als &lt;code class=&quot;language-text&quot;&gt;launch_gapps_avd_api32.sh&lt;/code&gt; (oder ähnlich). Achte darauf, dass es ein &lt;strong&gt;API-32&lt;/strong&gt;-AVD verwendet (es erstellt eines namens &lt;code class=&quot;language-text&quot;&gt;gplay_32_arm&lt;/code&gt;, falls es nicht existiert) und explizit &lt;code class=&quot;language-text&quot;&gt;-packet-streamer-endpoint localhost:8554&lt;/code&gt; nutzt. Mach es ausführbar (&lt;code class=&quot;language-text&quot;&gt;chmod +x launch_gapps_avd_api32.sh&lt;/code&gt;).&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Startskript ausführen:&lt;/strong&gt; Öffne ein &lt;em&gt;neues&lt;/em&gt; Terminal und führe das Skript aus:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;./launch_gapps_avd_api32.sh&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Verifizieren:&lt;/strong&gt; Sobald der Emulator gebootet ist:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Prüfe das &lt;code class=&quot;language-text&quot;&gt;bumble-hci-bridge&lt;/code&gt;-Terminal auf gRPC- und HCI-Traffic.&lt;/li&gt;
&lt;li&gt;Gehe zu Android Settings -&gt; Bluetooth und schalte es ein.&lt;/li&gt;
&lt;li&gt;Versuche zu scannen oder zu koppeln.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;Das erfolgreiche Startskript (API 32, expliziter Netsim-Endpunkt)&lt;/h2&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token shebang important&quot;&gt;#!/bin/bash&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# Script to setup and launch a Google Play Android emulator (API 32) on macOS M1/ARM64&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;# Uses explicit Netsim endpoint for Bumble bridge compatibility.&lt;/span&gt;

&lt;span class=&quot;token builtin class-name&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-e&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;# Exit immediately if a command exits with a non-zero status.&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# --- Configuration ---&lt;/span&gt;
&lt;span class=&quot;token assign-left variable&quot;&gt;API_LEVEL&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;32&quot;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;# Target Android API Level (Android 12L)&lt;/span&gt;
&lt;span class=&quot;token assign-left variable&quot;&gt;AVD_NAME&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;gplay_&lt;span class=&quot;token variable&quot;&gt;${API_LEVEL}&lt;/span&gt;_arm&quot;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;# Name for the Android Virtual Device&lt;/span&gt;
&lt;span class=&quot;token assign-left variable&quot;&gt;IMAGE_TAG&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;google_apis_playstore&quot;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;# Image type with Google Play Store&lt;/span&gt;
&lt;span class=&quot;token assign-left variable&quot;&gt;ARCH&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;arm64-v8a&quot;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;# Architecture for Apple Silicon&lt;/span&gt;
&lt;span class=&quot;token assign-left variable&quot;&gt;SYSTEM_IMAGE_PKG&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;system-images;android-&lt;span class=&quot;token variable&quot;&gt;${API_LEVEL}&lt;/span&gt;;&lt;span class=&quot;token variable&quot;&gt;${IMAGE_TAG}&lt;/span&gt;;&lt;span class=&quot;token variable&quot;&gt;${ARCH}&lt;/span&gt;&quot;&lt;/span&gt;
&lt;span class=&quot;token assign-left variable&quot;&gt;DEVICE_DEF&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;pixel_6a&quot;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;# A common modern Pixel device definition&lt;/span&gt;
&lt;span class=&quot;token assign-left variable&quot;&gt;BUMBLE_NETSIM_GRPC_PORT&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;8554&quot;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;# Port where bumble-hci-bridge Netsim controller is listening&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# --- Find Android SDK ---&lt;/span&gt;
&lt;span class=&quot;token assign-left variable&quot;&gt;ANDROID_SDK_ROOT&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;${ANDROID_HOME&lt;span class=&quot;token operator&quot;&gt;:-&lt;/span&gt;${ANDROID_SDK_ROOT&lt;span class=&quot;token operator&quot;&gt;:-&lt;/span&gt;$HOME&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;Library&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;Android&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;sdk}&lt;/span&gt;}&quot;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-d&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$ANDROID_SDK_ROOT&lt;/span&gt;&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;then&lt;/span&gt;
    &lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;❌ Error: Android SDK not found at &apos;&lt;span class=&quot;token variable&quot;&gt;$ANDROID_SDK_ROOT&lt;/span&gt;&apos;&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token file-descriptor important&quot;&gt;&amp;amp;2&lt;/span&gt;
    &lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;   Please install Android Studio or set ANDROID_HOME/ANDROID_SDK_ROOT.&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token file-descriptor important&quot;&gt;&amp;amp;2&lt;/span&gt;
    &lt;span class=&quot;token builtin class-name&quot;&gt;exit&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;fi&lt;/span&gt;
&lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;▶️ Using Android SDK at: &lt;span class=&quot;token variable&quot;&gt;$ANDROID_SDK_ROOT&lt;/span&gt;&quot;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# --- Define Tool Paths ---&lt;/span&gt;
&lt;span class=&quot;token assign-left variable&quot;&gt;CMDLINE_TOOLS_BIN&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$ANDROID_SDK_ROOT&lt;/span&gt;/cmdline-tools/latest/bin&quot;&lt;/span&gt;
&lt;span class=&quot;token assign-left variable&quot;&gt;PLATFORM_TOOLS_DIR&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$ANDROID_SDK_ROOT&lt;/span&gt;/platform-tools&quot;&lt;/span&gt;
&lt;span class=&quot;token assign-left variable&quot;&gt;EMULATOR_DIR&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$ANDROID_SDK_ROOT&lt;/span&gt;/emulator&quot;&lt;/span&gt;

&lt;span class=&quot;token assign-left variable&quot;&gt;SDKMANAGER&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$CMDLINE_TOOLS_BIN&lt;/span&gt;/sdkmanager&quot;&lt;/span&gt;
&lt;span class=&quot;token assign-left variable&quot;&gt;AVDMANAGER&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$CMDLINE_TOOLS_BIN&lt;/span&gt;/avdmanager&quot;&lt;/span&gt;
&lt;span class=&quot;token assign-left variable&quot;&gt;EMULATOR&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$EMULATOR_DIR&lt;/span&gt;/emulator&quot;&lt;/span&gt;
&lt;span class=&quot;token assign-left variable&quot;&gt;ADB&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$PLATFORM_TOOLS_DIR&lt;/span&gt;/adb&quot;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# --- Check Essential Tools ---&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt; &lt;span class=&quot;token builtin class-name&quot;&gt;command&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-v&lt;/span&gt; sdkmanager &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&gt;&lt;/span&gt; /dev/null&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;then&lt;/span&gt; &lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;❌ Error: sdkmanager not found. Check SDK Command-line Tools installation and PATH.&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token file-descriptor important&quot;&gt;&amp;amp;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token builtin class-name&quot;&gt;exit&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;fi&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt; &lt;span class=&quot;token builtin class-name&quot;&gt;command&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-v&lt;/span&gt; avdmanager &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&gt;&lt;/span&gt; /dev/null&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;then&lt;/span&gt; &lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;❌ Error: avdmanager not found. Check SDK Command-line Tools installation and PATH.&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token file-descriptor important&quot;&gt;&amp;amp;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token builtin class-name&quot;&gt;exit&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;fi&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt; &lt;span class=&quot;token builtin class-name&quot;&gt;command&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-v&lt;/span&gt; emulator &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&gt;&lt;/span&gt; /dev/null&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;then&lt;/span&gt; &lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;❌ Error: emulator not found. Check Android Emulator installation and PATH.&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token file-descriptor important&quot;&gt;&amp;amp;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token builtin class-name&quot;&gt;exit&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;fi&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt; &lt;span class=&quot;token builtin class-name&quot;&gt;command&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-v&lt;/span&gt; adb &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&gt;&lt;/span&gt; /dev/null&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;then&lt;/span&gt; &lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;❌ Error: adb not found. Check SDK Platform-Tools installation and PATH.&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token file-descriptor important&quot;&gt;&amp;amp;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token builtin class-name&quot;&gt;exit&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;fi&lt;/span&gt;
&lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;✅ Basic SDK tools found in PATH.&quot;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# --- Stop Currently Running Emulators ---&lt;/span&gt;
&lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;▶️ Stopping any currently running emulators...&quot;&lt;/span&gt;
&lt;span class=&quot;token assign-left variable&quot;&gt;RUNNING_EMULATORS&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$(&lt;/span&gt;adb devices &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;grep&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;emulator-&apos;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;cut&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-f1&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-n&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$RUNNING_EMULATORS&lt;/span&gt;&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;then&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token for-or-select variable&quot;&gt;emu_id&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$RUNNING_EMULATORS&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;do&lt;/span&gt;
        &lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;   Stopping &lt;span class=&quot;token variable&quot;&gt;$emu_id&lt;/span&gt;...&quot;&lt;/span&gt;
        adb &lt;span class=&quot;token parameter variable&quot;&gt;-s&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$emu_id&lt;/span&gt;&quot;&lt;/span&gt; emu &lt;span class=&quot;token function&quot;&gt;kill&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;   (Failed to kill &lt;span class=&quot;token variable&quot;&gt;$emu_id&lt;/span&gt;, may already be stopped)&quot;&lt;/span&gt;
        &lt;span class=&quot;token function&quot;&gt;sleep&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;# Small delay&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;done&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;# Give ADB server time to recognize disconnection&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;sleep&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;
    &lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;   Emulators stopped.&quot;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt;
    &lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;   No emulators appear to be running according to &apos;adb devices&apos;.&quot;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;fi&lt;/span&gt;
&lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;✅ Emulator check/stop finished.&quot;&lt;/span&gt;


&lt;span class=&quot;token comment&quot;&gt;# --- Install/Update Required SDK Packages ---&lt;/span&gt;
&lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;▶️ Ensuring required SDK packages are installed...&quot;&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;# Accept licenses non-interactively&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;yes&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; sdkmanager &lt;span class=&quot;token parameter variable&quot;&gt;--licenses&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; /dev/null &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;   (Ignoring potential license script errors)&quot;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# Install platform-tools, emulator&lt;/span&gt;
sdkmanager &lt;span class=&quot;token string&quot;&gt;&quot;platform-tools&quot;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;emulator&quot;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# Install the Google Play system image for API 32&lt;/span&gt;
&lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;▶️ Attempting to install/update Google Play system image: &lt;span class=&quot;token variable&quot;&gt;$SYSTEM_IMAGE_PKG&lt;/span&gt;&quot;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt; sdkmanager &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$SYSTEM_IMAGE_PKG&lt;/span&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;then&lt;/span&gt;
    &lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;❌ Error: Failed to install required system image &apos;&lt;span class=&quot;token variable&quot;&gt;$SYSTEM_IMAGE_PKG&lt;/span&gt;&apos;.&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token file-descriptor important&quot;&gt;&amp;amp;2&lt;/span&gt;
    &lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;   Please check available images using: sdkmanager --list | grep &apos;system-images;android-&lt;span class=&quot;token variable&quot;&gt;${API_LEVEL}&lt;/span&gt;;.*&lt;span class=&quot;token variable&quot;&gt;${ARCH}&lt;/span&gt;&apos;&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token file-descriptor important&quot;&gt;&amp;amp;2&lt;/span&gt;
    &lt;span class=&quot;token builtin class-name&quot;&gt;exit&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;fi&lt;/span&gt;
&lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;✅ System image package &apos;&lt;span class=&quot;token variable&quot;&gt;$SYSTEM_IMAGE_PKG&lt;/span&gt;&apos; present.&quot;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# --- Check if AVD Exists, Create ONLY if Missing ---&lt;/span&gt;
&lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;▶️ Ensuring AVD &apos;&lt;span class=&quot;token variable&quot;&gt;$AVD_NAME&lt;/span&gt;&apos; exists...&quot;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt; avdmanager list avd &lt;span class=&quot;token parameter variable&quot;&gt;--compact&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;grep&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-q&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;^&lt;span class=&quot;token variable&quot;&gt;${AVD_NAME}&lt;/span&gt;$&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;then&lt;/span&gt;
    &lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;   AVD &apos;&lt;span class=&quot;token variable&quot;&gt;$AVD_NAME&lt;/span&gt;&apos; not found. Creating...&quot;&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;# Using &apos;echo no&apos; usually prevents hardware profile creation prompts. Pipe empty string for potential licenses.&lt;/span&gt;
    &lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; avdmanager create avd &lt;span class=&quot;token parameter variable&quot;&gt;--force&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;--name&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$AVD_NAME&lt;/span&gt;&quot;&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;--package&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$SYSTEM_IMAGE_PKG&lt;/span&gt;&quot;&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;--device&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$DEVICE_DEF&lt;/span&gt;&quot;&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;--sdcard&lt;/span&gt; 512M &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
         &lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;❌ Error: Failed to create AVD &apos;&lt;span class=&quot;token variable&quot;&gt;$AVD_NAME&lt;/span&gt;&apos;.&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token file-descriptor important&quot;&gt;&amp;amp;2&lt;/span&gt;
         &lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;   Maybe the device definition &apos;&lt;span class=&quot;token variable&quot;&gt;$DEVICE_DEF&lt;/span&gt;&apos; is invalid for this image?&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token file-descriptor important&quot;&gt;&amp;amp;2&lt;/span&gt;
         &lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;   Check available devices: avdmanager list device&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token file-descriptor important&quot;&gt;&amp;amp;2&lt;/span&gt;
         &lt;span class=&quot;token builtin class-name&quot;&gt;exit&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;✅ AVD &apos;&lt;span class=&quot;token variable&quot;&gt;$AVD_NAME&lt;/span&gt;&apos; created.&quot;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt;
    &lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;✅ AVD &apos;&lt;span class=&quot;token variable&quot;&gt;$AVD_NAME&lt;/span&gt;&apos; already exists. Will reuse.&quot;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;fi&lt;/span&gt;


&lt;span class=&quot;token comment&quot;&gt;# --- Launch Emulator ---&lt;/span&gt;
&lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;▶️ Launching existing/new emulator: &apos;&lt;span class=&quot;token variable&quot;&gt;$AVD_NAME&lt;/span&gt;&apos; (pointing to Bumble Netsim bridge on localhost:&lt;span class=&quot;token variable&quot;&gt;$BUMBLE_NETSIM_GRPC_PORT&lt;/span&gt;)...&quot;&lt;/span&gt;
&lt;span class=&quot;token assign-left variable&quot;&gt;EMULATOR_LOG&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;emulator-&lt;span class=&quot;token variable&quot;&gt;$AVD_NAME&lt;/span&gt;.log&quot;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;# Log file name updated for API 32 AVD&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;# Google Play images often need a writable system partition&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;# Explicitly point packet streamer to localhost:8554 where bridge is listening&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;nohup&lt;/span&gt; emulator &lt;span class=&quot;token parameter variable&quot;&gt;-avd&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$AVD_NAME&lt;/span&gt;&quot;&lt;/span&gt; -no-snapshot-load &lt;span class=&quot;token parameter variable&quot;&gt;-gpu&lt;/span&gt; auto -show-kernel -writable-system &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
    -packet-streamer-endpoint &lt;span class=&quot;token string&quot;&gt;&quot;localhost:&lt;span class=&quot;token variable&quot;&gt;$BUMBLE_NETSIM_GRPC_PORT&lt;/span&gt;&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$EMULATOR_LOG&lt;/span&gt;&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&lt;span class=&quot;token file-descriptor important&quot;&gt;2&lt;/span&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token file-descriptor important&quot;&gt;&amp;amp;1&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;
&lt;span class=&quot;token assign-left variable&quot;&gt;EMULATOR_PID&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$!&lt;/span&gt;
&lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;   Emulator starting in background (PID: &lt;span class=&quot;token variable&quot;&gt;$EMULATOR_PID&lt;/span&gt;). Log: &lt;span class=&quot;token variable&quot;&gt;$EMULATOR_LOG&lt;/span&gt;&quot;&lt;/span&gt;
&lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-n&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;   Waiting for emulator to appear in ADB...&quot;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# Wait for the emulator device to show up in adb&lt;/span&gt;
&lt;span class=&quot;token assign-left variable&quot;&gt;WAIT_ADB_TIMEOUT&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;90&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;# Increase timeout slightly for GPlay images&lt;/span&gt;
&lt;span class=&quot;token assign-left variable&quot;&gt;&lt;span class=&quot;token environment constant&quot;&gt;SECONDS&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;
&lt;span class=&quot;token assign-left variable&quot;&gt;EMULATOR_ID&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&quot;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;# Reset variable&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;token environment constant&quot;&gt;$SECONDS&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-lt&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$WAIT_ADB_TIMEOUT&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;# Find the *new* emulator ID&lt;/span&gt;
    &lt;span class=&quot;token assign-left variable&quot;&gt;CURRENT_EMU_ID&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$(&lt;/span&gt;adb devices &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;grep&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;emulator-&apos;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;head&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-n&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;cut&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-f1&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-n&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$CURRENT_EMU_ID&lt;/span&gt;&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;then&lt;/span&gt;
        &lt;span class=&quot;token assign-left variable&quot;&gt;EMULATOR_ID&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$CURRENT_EMU_ID&lt;/span&gt;&quot;&lt;/span&gt;
        &lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot; Found (&lt;span class=&quot;token variable&quot;&gt;$EMULATOR_ID&lt;/span&gt;)!&quot;&lt;/span&gt;
        &lt;span class=&quot;token builtin class-name&quot;&gt;break&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;fi&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;sleep&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;
    &lt;span class=&quot;token assign-left variable&quot;&gt;&lt;span class=&quot;token environment constant&quot;&gt;SECONDS&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$((&lt;/span&gt;SECONDS &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;))&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-n&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;.&quot;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;done&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-z&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$EMULATOR_ID&lt;/span&gt;&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;then&lt;/span&gt;
    &lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&quot;&lt;/span&gt;
    &lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;❌ Error: Emulator did not appear in ADB within &lt;span class=&quot;token variable&quot;&gt;$WAIT_ADB_TIMEOUT&lt;/span&gt; seconds.&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token file-descriptor important&quot;&gt;&amp;amp;2&lt;/span&gt;
    &lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;   Check logs: &lt;span class=&quot;token variable&quot;&gt;$EMULATOR_LOG&lt;/span&gt;&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token file-descriptor important&quot;&gt;&amp;amp;2&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;# Try to kill the process if it&apos;s still lingering&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;kill&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$EMULATOR_PID&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&lt;span class=&quot;token file-descriptor important&quot;&gt;2&lt;/span&gt;&gt;&lt;/span&gt;/dev/null &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;   (Emulator process &lt;span class=&quot;token variable&quot;&gt;$EMULATOR_PID&lt;/span&gt; may have already exited)&quot;&lt;/span&gt;
    &lt;span class=&quot;token builtin class-name&quot;&gt;exit&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;fi&lt;/span&gt;


&lt;span class=&quot;token comment&quot;&gt;# --- Wait for Boot Completion ---&lt;/span&gt;
&lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-n&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;▶️ Waiting for Android system to fully boot (Google Play images can take longer)...&quot;&lt;/span&gt;
&lt;span class=&quot;token assign-left variable&quot;&gt;BOOT_TIMEOUT&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;240&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;# Increase timeout significantly for GPlay images&lt;/span&gt;
&lt;span class=&quot;token assign-left variable&quot;&gt;&lt;span class=&quot;token environment constant&quot;&gt;SECONDS&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;token environment constant&quot;&gt;$SECONDS&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-lt&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$BOOT_TIMEOUT&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;# Check if device is online first&lt;/span&gt;
    &lt;span class=&quot;token assign-left variable&quot;&gt;DEVICE_STATE&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$(&lt;/span&gt;adb &lt;span class=&quot;token parameter variable&quot;&gt;-s&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$EMULATOR_ID&lt;/span&gt;&quot;&lt;/span&gt; get-state &lt;span class=&quot;token operator&quot;&gt;&lt;span class=&quot;token file-descriptor important&quot;&gt;2&lt;/span&gt;&gt;&lt;/span&gt;/dev/null&lt;span class=&quot;token variable&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$DEVICE_STATE&lt;/span&gt;&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;device&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;then&lt;/span&gt;
       &lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-n&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;s(&lt;span class=&quot;token variable&quot;&gt;$DEVICE_STATE&lt;/span&gt;)&quot;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;# State not ready&lt;/span&gt;
       &lt;span class=&quot;token function&quot;&gt;sleep&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt;
       &lt;span class=&quot;token assign-left variable&quot;&gt;&lt;span class=&quot;token environment constant&quot;&gt;SECONDS&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$((&lt;/span&gt;SECONDS &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;))&lt;/span&gt;&lt;/span&gt;
       &lt;span class=&quot;token builtin class-name&quot;&gt;continue&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;fi&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;# Check boot completed property&lt;/span&gt;
    &lt;span class=&quot;token assign-left variable&quot;&gt;BOOT_COMPLETED&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$(&lt;/span&gt;adb &lt;span class=&quot;token parameter variable&quot;&gt;-s&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$EMULATOR_ID&lt;/span&gt;&quot;&lt;/span&gt; shell getprop sys.boot_completed &lt;span class=&quot;token operator&quot;&gt;&lt;span class=&quot;token file-descriptor important&quot;&gt;2&lt;/span&gt;&gt;&lt;/span&gt;/dev/null &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;tr&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-d&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;
&apos;&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$BOOT_COMPLETED&lt;/span&gt;&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;1&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;then&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;# Double check package manager is ready too for GPlay images&lt;/span&gt;
        &lt;span class=&quot;token assign-left variable&quot;&gt;PM_READY&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$(&lt;/span&gt;adb &lt;span class=&quot;token parameter variable&quot;&gt;-s&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$EMULATOR_ID&lt;/span&gt;&quot;&lt;/span&gt; shell pm get-install-location &lt;span class=&quot;token operator&quot;&gt;&lt;span class=&quot;token file-descriptor important&quot;&gt;2&lt;/span&gt;&gt;&lt;/span&gt;/dev/null&lt;span class=&quot;token variable&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$PM_READY&lt;/span&gt;&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; *&lt;span class=&quot;token string&quot;&gt;&quot;0&quot;&lt;/span&gt;* &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$PM_READY&lt;/span&gt;&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; *&lt;span class=&quot;token string&quot;&gt;&quot;1&quot;&lt;/span&gt;* &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$PM_READY&lt;/span&gt;&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; *&lt;span class=&quot;token string&quot;&gt;&quot;2&quot;&lt;/span&gt;* &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;then&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;# Check if pm command gives valid output&lt;/span&gt;
            &lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot; Booted!&quot;&lt;/span&gt;
            &lt;span class=&quot;token builtin class-name&quot;&gt;break&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt;
            &lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-n&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;p(pm not ready)&quot;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;# Package Manager not ready&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;fi&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt;
         &lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-n&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;b(booting)&quot;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;# Boot not completed&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;fi&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;sleep&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt;
    &lt;span class=&quot;token assign-left variable&quot;&gt;&lt;span class=&quot;token environment constant&quot;&gt;SECONDS&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$((&lt;/span&gt;SECONDS &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;))&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;done&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;token environment constant&quot;&gt;$SECONDS&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-ge&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$BOOT_TIMEOUT&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;then&lt;/span&gt;
    &lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&quot;&lt;/span&gt;
    &lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;❌ Error: Android system did not fully boot within &lt;span class=&quot;token variable&quot;&gt;$BOOT_TIMEOUT&lt;/span&gt; seconds.&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token file-descriptor important&quot;&gt;&amp;amp;2&lt;/span&gt;
    &lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;   Emulator might be stuck. Check logs (&lt;span class=&quot;token variable&quot;&gt;$EMULATOR_LOG&lt;/span&gt;) or try launching manually.&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token file-descriptor important&quot;&gt;&amp;amp;2&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;# Don&apos;t exit here, user might want to interact with stuck emulator&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;fi&lt;/span&gt;


&lt;span class=&quot;token comment&quot;&gt;# --- Final Instructions ---&lt;/span&gt;
&lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;---&quot;&lt;/span&gt;
&lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;✅ Google Play Emulator &apos;&lt;span class=&quot;token variable&quot;&gt;$AVD_NAME&lt;/span&gt;&apos; (API &lt;span class=&quot;token variable&quot;&gt;$API_LEVEL&lt;/span&gt;) (&lt;span class=&quot;token variable&quot;&gt;$EMULATOR_ID&lt;/span&gt;) should be running.&quot;&lt;/span&gt;
&lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;   Bluetooth should be using the Netsim backend at localhost:&lt;span class=&quot;token variable&quot;&gt;$BUMBLE_NETSIM_GRPC_PORT&lt;/span&gt; (intercepted by Bumble).&quot;&lt;/span&gt;
&lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;   Connect shell:   adb -s &lt;span class=&quot;token variable&quot;&gt;$EMULATOR_ID&lt;/span&gt; shell&quot;&lt;/span&gt;
&lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;   Install APK:     adb -s &lt;span class=&quot;token variable&quot;&gt;$EMULATOR_ID&lt;/span&gt; install /path/to/your.apk&quot;&lt;/span&gt;
&lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;   Stop emulator:   adb -s &lt;span class=&quot;token variable&quot;&gt;$EMULATOR_ID&lt;/span&gt; emu kill&quot;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-n&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$EMULATOR_PID&lt;/span&gt;&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;then&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;# Only show PID if we launched it&lt;/span&gt;
    &lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;   Kill Process:    kill &lt;span class=&quot;token variable&quot;&gt;$EMULATOR_PID&lt;/span&gt;&quot;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;fi&lt;/span&gt;
&lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;   NOTE: Google Play Services may need updates inside the emulator.&quot;&lt;/span&gt;
&lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;---&quot;&lt;/span&gt;

&lt;span class=&quot;token builtin class-name&quot;&gt;exit&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2&gt;Zentrale Erkenntnisse für M1-Mac + Emulator + Bumble&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;API-Level zählt:&lt;/strong&gt; Neuer ist für Emulator-Kompatibilität nicht immer besser, besonders bei komplexen Funktionen wie Bluetooth-Bridging. API 32 wirkte in meinen Tests dafür stabiler als API 34.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Explizite Endpunkte:&lt;/strong&gt; Verlass dich nicht auf &lt;code class=&quot;language-text&quot;&gt;-packet-streamer-endpoint default&lt;/code&gt;, wenn du eine externe Bridge wie Bumbles Netsim-Controller-Modus verwendest. Zeige dem Emulator explizit auf &lt;code class=&quot;language-text&quot;&gt;localhost:&amp;lt;port&gt;&lt;/code&gt;, wo deine Bridge lauscht.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Netsim-Bridge &gt; QEMU-Socket:&lt;/strong&gt; Der &lt;code class=&quot;language-text&quot;&gt;android-netsim&lt;/code&gt;-Bridge-Modus scheint mit modernen Emulatoren eher zu funktionieren als die niedrigere &lt;code class=&quot;language-text&quot;&gt;-qemu -chardev socket&lt;/code&gt;-Methode, auch wenn die Socket-Methode &lt;em&gt;eine&lt;/em&gt; TCP-Verbindung herstellen kann.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code class=&quot;language-text&quot;&gt;usb:0&lt;/code&gt; vs. VID:PID:&lt;/strong&gt; Auf macOS/M1 kann die Identifikation von USB-Geräten eigenwillig sein. Wenn die exakte VID:PID unerwartet scheitert, versuche den Index &lt;code class=&quot;language-text&quot;&gt;usb:0&lt;/code&gt; (vorausgesetzt, es ist das primäre/beabsichtigte Gerät).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Hartnäckigkeit zahlt sich aus:&lt;/strong&gt; Das brauchte mehrere Versuche und die Kombination aus Dokumentation, Websuchen und iterativem Testen. Gib nicht zu schnell auf!&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Ich hoffe, dass diese konkrete, funktionierende Konfiguration anderen Entwicklern Stunden an Frust erspart. Viel Spaß beim Coden (und Bridgen)!&lt;/p&gt;</content:encoded></item><item><title><![CDATA[KI-Blog-Redesign: Wie Claude Code meine Gatsby-Seite verwandelte]]></title><description><![CDATA[Als Entwickler, der die meiste Zeit im Backend verbringt, habe ich mit Design schon immer gerungen. Mein persoenliches Blog war funktional…]]></description><link>https://bdteo.com/de/claude-code-transformed-my-blog-design-in-minutes/</link><guid isPermaLink="false">https://bdteo.com/de/claude-code-transformed-my-blog-design-in-minutes/</guid><pubDate>Sat, 12 Apr 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Als Entwickler, der die meiste Zeit im Backend verbringt, habe ich mit Design schon immer gerungen. Mein persoenliches Blog war funktional, sah aber aus, als sei es 2010 steckengeblieben: einfaches Styling, inkonsistente Abstaende und ein Farbschema, das man grosszuegig als &quot;minimal&quot; haette beschreiben koennen. Ich wollte es seit Monaten ueberarbeiten, aber der Gedanke, in CSS und Designsysteme einzutauchen, machte mir Albtraeume.&lt;/p&gt;
&lt;p&gt;Dann probierte ich Claude Code aus, den KI-Coding-Assistenten von Anthropic, und meine Perspektive veraenderte sich komplett.&lt;/p&gt;
&lt;h2&gt;Die Herausforderung: Mein trauriges, veraltetes Blog&lt;/h2&gt;
&lt;p&gt;Mein Blog ist mit Gatsby.js gebaut, und obwohl der Inhalt solide war, liess die Praesentation ihn im Stich:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Inkonsistente Abstaende auf der gesamten Website&lt;/li&gt;
&lt;li&gt;Schlechtes responsives Verhalten auf Mobilgeraeten&lt;/li&gt;
&lt;li&gt;Eine Dark-Mode-Implementierung, die kaum funktionierte&lt;/li&gt;
&lt;li&gt;Typografie, die bestenfalls amateurhaft aussah&lt;/li&gt;
&lt;li&gt;Kein zusammenhaengendes Farbsystem oder eine erkennbare Designsprache&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Ich hatte eine vage Vorstellung davon, was ich wollte: etwas Modernes, Sauberes und Professionelles, das meine Inhalte zum Leuchten bringen wuerde. Aber diese Vorstellung in tatsaechliches CSS zu uebersetzen? Genau dort renne ich normalerweise gegen eine Wand.&lt;/p&gt;
&lt;h2&gt;Claude Code betritt die Buehne: Mein virtueller Designpartner&lt;/h2&gt;
&lt;p&gt;Ich hatte schon eine Weile von Claude Code gehoert, war aber skeptisch, wie sehr eine KI bei Designarbeit wirklich helfen kann. Schliesslich braucht Design Geschmack und ein Auge fuer Aesthetik, nicht nur technisches Wissen.&lt;/p&gt;
&lt;p&gt;Ich beschloss trotzdem, es zu versuchen, mit einem einfachen Prompt: &quot;Please improve my styling by a ton.&quot; Ich war bereit, eine Stunde meiner Zeit und die ungefaehr $15 API-Kosten zu investieren, um zu sehen, was moeglich war.&lt;/p&gt;
&lt;p&gt;Was danach passierte, ueberraschte mich wirklich.&lt;/p&gt;
&lt;h2&gt;Der Transformationsprozess&lt;/h2&gt;
&lt;p&gt;Statt nur ein paar CSS-Anpassungen vorzuschlagen, ging Claude Code das Redesign mit der Gruendlichkeit eines professionellen Entwicklers an:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Analysephase&lt;/strong&gt;: Zuerst untersuchte es meine bestehenden Styles, die Komponentenarchitektur und das Farbschema, um zu verstehen, womit es arbeitete.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Erstellung eines Designsystems&lt;/strong&gt;: Statt oberflaechliche Aenderungen zu machen, baute es ein umfassendes Designsystem, darunter:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Eine professionelle Farbpalette mit semantischen Variablen fuer Light und Dark Mode&lt;/li&gt;
&lt;li&gt;Eine modernisierte Typografieskala mit sauberen responsiven Anpassungen&lt;/li&gt;
&lt;li&gt;Ein konsistentes Abstandssystem auf Basis eines 4px-Rasters&lt;/li&gt;
&lt;li&gt;Standardisierte Schatten, Border Radii und Transitionen&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Komponenten-Ueberarbeitung&lt;/strong&gt;: Es schrieb meine Komponenten nach modernen Best Practices um:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Erstellte ein responsives, kartenbasiertes Layout fuer Blogposts&lt;/li&gt;
&lt;li&gt;Implementierte einen Sticky Header mit Backdrop-Blur-Effekten&lt;/li&gt;
&lt;li&gt;Entwarf einen eleganten Theme-Umschalter&lt;/li&gt;
&lt;li&gt;Fuegte eine saubere Navigation mit aktiven Zustaenden hinzu&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Verbesserungen der Barrierefreiheit&lt;/strong&gt;: Claude Code machte die Dinge nicht nur schoener, sondern auch zugaenglicher:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Fuegte ordentliche Unterstuetzung fuer Tastaturnavigation hinzu&lt;/li&gt;
&lt;li&gt;Implementierte einen Skip-to-content-Link&lt;/li&gt;
&lt;li&gt;Stellte ausreichenden Farbkontrast sicher&lt;/li&gt;
&lt;li&gt;Fuegte passende ARIA-Labels hinzu&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Performance-Optimierung&lt;/strong&gt;: Der Code wurde auf Performance optimiert:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Importierte selektiv nur die notwendigen Bootstrap-Komponenten&lt;/li&gt;
&lt;li&gt;Optimierte die CSS-Organisation&lt;/li&gt;
&lt;li&gt;Fuegte hardwarebeschleunigte Animationen hinzu&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;Die umwerfenden Ergebnisse&lt;/h2&gt;
&lt;p&gt;Die Transformation war atemberaubend. In ungefaehr einer Stunde Hin und Her und fuer gerade einmal $15 an Claude-API-Kosten entwickelte sich mein Blog von etwas, das wie das Nebenprojekt eines Entwicklers aussah, zu einer professionell gestalteten Publikation. Das Redesign enthielt:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Ein ausgefeiltes Farbsystem mit sauberer Dark-Mode-Unterstuetzung&lt;/li&gt;
&lt;li&gt;Schoene Typografie, die sich perfekt ueber Geraete hinweg skaliert&lt;/li&gt;
&lt;li&gt;Professionelle Kartenlayouts fuer Blogposts&lt;/li&gt;
&lt;li&gt;Sanfte Animationen und Transitionen&lt;/li&gt;
&lt;li&gt;Einen Sticky Header mit glasartigen Effekten&lt;/li&gt;
&lt;li&gt;Konsistente Abstaende auf der gesamten Website&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Um das einzuordnen: Einen professionellen Webdesigner fuer Arbeit auf diesem Niveau zu beauftragen, wuerde wahrscheinlich $1,000-2,000 kosten und Tage oder Wochen an Hin und Her benoetigen. Stattdessen investierte ich eine Stunde meiner Zeit und $15 an API-Kosten.&lt;/p&gt;
&lt;p&gt;Aber was mich am meisten beeindruckte, war nicht nur die visuelle Verbesserung oder die Kostenersparnis. Es war die Qualitaet des Codes. Claude Code warf nicht einfach ein paar CSS-Hacks zusammen; es erstellte ein umfassendes, wartbares Designsystem, auf dem ich leicht weiter aufbauen kann.&lt;/p&gt;
&lt;h2&gt;Was ich ueber KI-unterstuetztes Design gelernt habe&lt;/h2&gt;
&lt;p&gt;Diese Erfahrung veraenderte meine Sicht darauf, was KI fuer Entwickler tun kann:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;KI ist stark im systematischen Denken&lt;/strong&gt;: Claude Code machte die Dinge nicht nur &quot;huebscher&quot;, sondern schuf ein zusammenhaengendes System mit Variablen, konsistenten Mustern und sauberer Organisation.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Sie ueberbrueckt Wissensluecken&lt;/strong&gt;: Als jemand ohne tiefes CSS-Fachwissen fuellte Claude Code meine Wissensluecken mit modernen Best Practices, von denen ich nicht gewusst haette, dass ich sie implementieren sollte.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Der Code ist produktionsreif&lt;/strong&gt;: Das Styling-Framework, das es erstellte, ist nicht nur Show. Es ist wartbarer, erweiterbarer Code, der modernen Standards folgt.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Es bewahrte meine Inhalte und Struktur&lt;/strong&gt;: Claude Code verbesserte das Design, waehrend es die Kernstruktur und die Inhalte meines Blogs beibehielt.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;Die Zukunft der Zusammenarbeit zwischen Entwicklern und KI&lt;/h2&gt;
&lt;p&gt;Meine Erfahrung zeigte mir etwas Wichtiges: Claude Code ersetzt Entwickler nicht, sondern erweitert unsere Faehigkeiten. Es half mir, meine persoenlichen Grenzen im Frontend-Design zu ueberwinden, waehrend ich weiterhin die Kontrolle ueber die Gesamtrichtung behielt.&lt;/p&gt;
&lt;p&gt;Fuer Entwickler, die in manchen Bereichen staerker sind als in anderen (und sind wir das nicht alle?), koennen KI-Assistenten wie Claude Code helfen, die Luecken in unseren Faehigkeiten zu schliessen.&lt;/p&gt;
&lt;h2&gt;Probier es selbst aus&lt;/h2&gt;
&lt;p&gt;Wenn du mit Design oder irgendeinem anderen Aspekt der Entwicklung kaempfst, empfehle ich dir sehr, Claude Code auszuprobieren. Die Erfahrung veraenderte nicht nur mein Blog, sondern auch meine Vorstellung davon, was mit KI-unterstuetzter Entwicklung moeglich ist.&lt;/p&gt;
&lt;p&gt;Hast du Claude Code oder aehnliche KI-Coding-Assistenten ausprobiert? Ich wuerde unten in den Kommentaren gern von deinen Erfahrungen hoeren.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Hinweis: Dieses Blog selbst wurde mit Claude Code neu gestaltet, also ist das, was du gerade siehst, das Ergebnis des Prozesses, den ich beschrieben habe. Die Transformation von meinem frueheren Design zu diesem aktuellen dauerte ungefaehr eine Stunde Prompting und Iteration und kostete etwa $15 an Claude-API-Nutzung. Wenn man traditionelle Designkosten betrachtet, ist das ein unglaubliches Wertversprechen.&lt;/em&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Huawei Watch D2 BLE-Pairing: Protokoll- und Vendor-Lock-in-Fall]]></title><description><![CDATA[TL;DR: Die Huawei Watch D2 nutzt kein Standard-BLE-Pairing. Stattdessen verlangt sie einen proprietären 11-Schritte-Handshake mit eigenen…]]></description><link>https://bdteo.com/de/huawei-watch-d2-proprietary-protocol-vendor-lockin/</link><guid isPermaLink="false">https://bdteo.com/de/huawei-watch-d2-proprietary-protocol-vendor-lockin/</guid><pubDate>Fri, 11 Apr 2025 00:00:00 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;TL;DR:&lt;/strong&gt; Die Huawei Watch D2 nutzt kein Standard-BLE-Pairing. Stattdessen verlangt sie einen proprietären 11-Schritte-Handshake mit eigenen GATT-Characteristics, HMAC-SHA256-Schlüsselableitung aus einem QR-Code und Verschlüsselung auf Anwendungsebene. Das ist Vendor Lock-in per Design - es zwingt dich in Huaweis Health-App. Die gute Nachricht: Die Community hat es reverse-engineered. Gadgetbridge unterstützt die Watch D2 inzwischen, und Open-Source-Implementierungen wie &lt;code class=&quot;language-text&quot;&gt;huawei-lpv2&lt;/code&gt; existieren. Auch der EU DMA beginnt, dagegenzuhalten.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Ich hatte Standard-Bluetooth-Pairing erwartet. Verbinden, bonden, Daten austauschen - das Übliche. Was ich stattdessen bekam, war ein proprietärer kryptografischer Handshake, dessen Reverse Engineering Wochen dauerte.&lt;/p&gt;
&lt;p&gt;Das passierte beim Bau von D2Explorer - meinem Projekt, um die Huawei Watch D2 ohne Huaweis Health-App mit Linux und macOS zu verbinden. Nachdem ich &lt;a href=&quot;/bluez-pairing-python-agent-workaround-authentication-failed/&quot;&gt;BlueZs Pairing-Agent-Probleme gelöst&lt;/a&gt; und auf die plattformübergreifende SimpleBLE-Bibliothek migriert hatte, dachte ich, der schwierige Teil sei vorbei. Der schwierige Teil hatte noch nicht angefangen.&lt;/p&gt;
&lt;h2&gt;Was man erwarten würde: Standard-BLE-Pairing&lt;/h2&gt;
&lt;p&gt;So soll Bluetooth-LE-Pairing &lt;em&gt;eigentlich&lt;/em&gt; funktionieren:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Nach dem Gerät über seinen beworbenen Namen scannen (z. B. &quot;HUAWEI WATCH D2-CA0&quot;).&lt;/li&gt;
&lt;li&gt;Mit &lt;code class=&quot;language-text&quot;&gt;peripheral.connect()&lt;/code&gt; verbinden.&lt;/li&gt;
&lt;li&gt;Das Betriebssystem erledigt Pairing/Bonding - PIN-Abfrage, Just Works, was auch immer die Sicherheitsstufe verlangt.&lt;/li&gt;
&lt;li&gt;Sobald das Gerät gebondet ist, mit Standard- oder eigenen GATT-Services interagieren.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Das Betriebssystem verwaltet die Sicherheit. Deine Anwendung konzentriert sich auf Daten. Einfach.&lt;/p&gt;
&lt;h2&gt;Was tatsächlich passiert: Ein proprietärer 11-Schritte-Handshake&lt;/h2&gt;
&lt;p&gt;Was die Watch D2 tatsächlich verlangt, ist etwas völlig anderes. Die grundlegende BLE-Verbindung ist nur die Tür. Dahinter liegt ein eigenes Authentifizierungsprotokoll auf Anwendungsebene, das Huawei auf Standard-BLE gebaut hat - was die Community &lt;strong&gt;Huawei Link Protocol v2&lt;/strong&gt; nennt &lt;small&gt;&lt;a href=&quot;#ref1&quot;&gt;[1]&lt;/a&gt;&lt;/small&gt;.&lt;/p&gt;
&lt;p&gt;Standard-BLE-Pairing-Mechanismen werden vollständig umgangen. Um dich zu authentifizieren und auf irgendeine sinnvolle Art Daten zu bekommen, musst du diese Sequenz über eigene GATT-Characteristics abarbeiten:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Connect&lt;/strong&gt; - die grundlegende BLE-Verbindung herstellen.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Enable Notifications&lt;/strong&gt; - &lt;em&gt;sofort&lt;/em&gt; Notifications auf Characteristic &lt;code class=&quot;language-text&quot;&gt;0000fe02-...&lt;/code&gt; abonnieren. Das ist timingkritisch - verpasst du das Fenster, trennt die Uhr die Verbindung.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;GetLinkParams&lt;/strong&gt; - &lt;em&gt;sofort&lt;/em&gt; einen eigenen Command (Service ID &lt;code class=&quot;language-text&quot;&gt;0x0001&lt;/code&gt;, Command ID &lt;code class=&quot;language-text&quot;&gt;0x0001&lt;/code&gt;) an die Write-Characteristic &lt;code class=&quot;language-text&quot;&gt;0000fe01-...&lt;/code&gt; senden.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Receive Server Nonce&lt;/strong&gt; - auf eine Notification mit der zufälligen Challenge der Uhr warten.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Derive Secret Key&lt;/strong&gt; - eine Client-Nonce erzeugen. Server-Nonce, Client-Nonce und den &lt;strong&gt;numerischen Wert aus dem QR-Code der Uhr&lt;/strong&gt; kombinieren. HMAC-SHA256 ausführen (mit den Bytes des QR-Code-Werts als Schlüssel), um einen gemeinsamen &lt;code class=&quot;language-text&quot;&gt;secretKey_&lt;/code&gt; abzuleiten.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;AuthRequest&lt;/strong&gt; - Client-Nonce und HMAC-Digest (mit dem abgeleiteten &lt;code class=&quot;language-text&quot;&gt;secretKey_&lt;/code&gt;) zurück an die Uhr senden (Service &lt;code class=&quot;language-text&quot;&gt;0x0001&lt;/code&gt;, Command &lt;code class=&quot;language-text&quot;&gt;0x0002&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Verify Server Token&lt;/strong&gt; - den Authentifizierungstoken der Uhr empfangen. Ihn mit dem &lt;code class=&quot;language-text&quot;&gt;secretKey_&lt;/code&gt; und den ausgetauschten Nonces verifizieren.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;SetTime&lt;/strong&gt; - aktuelle Zeit und Zeitzonen-Offset senden, mit dem &lt;code class=&quot;language-text&quot;&gt;secretKey_&lt;/code&gt; &lt;em&gt;verschlüsselt&lt;/em&gt; (Service &lt;code class=&quot;language-text&quot;&gt;0x0002&lt;/code&gt;, Command &lt;code class=&quot;language-text&quot;&gt;0x0003&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;QrToken&lt;/strong&gt; - den QR-Code-Wert zurücksenden, mit dem &lt;code class=&quot;language-text&quot;&gt;secretKey_&lt;/code&gt; &lt;em&gt;verschlüsselt&lt;/em&gt; (Service &lt;code class=&quot;language-text&quot;&gt;0x0001&lt;/code&gt;, Command &lt;code class=&quot;language-text&quot;&gt;0x0004&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;AuthResult&lt;/strong&gt; - eine finale Bestätigung senden, mit dem &lt;code class=&quot;language-text&quot;&gt;secretKey_&lt;/code&gt; &lt;em&gt;verschlüsselt&lt;/em&gt; (Service &lt;code class=&quot;language-text&quot;&gt;0x0001&lt;/code&gt;, Command &lt;code class=&quot;language-text&quot;&gt;0x0005&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Done&lt;/strong&gt; - erst jetzt ist die Verbindung authentifiziert.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Eigene TLV-Nachrichtenformate. CRC-Prüfungen. Service- und Command-IDs. Verschlüsselung auf Anwendungsebene. Millisekundengenaues Timing. All das passiert &lt;em&gt;oberhalb&lt;/em&gt; des BLE-Stacks, unsichtbar für normale Bluetooth-Tools.&lt;/p&gt;
&lt;p&gt;Der QR-Code auf dem Bildschirm der Uhr ist das gemeinsame Geheimnis. Ohne ihn kannst du den Schlüssel nicht ableiten. Ohne den Schlüssel kannst du dich nicht authentifizieren. Ohne Authentifizierung gibt dir die Uhr nichts.&lt;/p&gt;
&lt;h2&gt;Warum Huawei das tut&lt;/h2&gt;
&lt;p&gt;Huawei könnte das als erhöhte Sicherheit darstellen. Der praktische Effekt ist &lt;strong&gt;Vendor Lock-in&lt;/strong&gt;.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Hohe Einstiegshürde&lt;/strong&gt; - das Protokoll ist undokumentiert. Eine Neuimplementierung verlangt Reverse Engineering der Huawei-Health-App (13.000+ Klassen, 64.000+ Methoden &lt;small&gt;&lt;a href=&quot;#ref2&quot;&gt;[2]&lt;/a&gt;&lt;/small&gt;) oder die Analyse von BLE-Traffic. Das schreckt Drittanbieter-Apps aktiv ab.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Keine Interoperabilität&lt;/strong&gt; - normale Fitness-Apps können sich nicht verbinden. Die Uhr schließt ihren Handshake nur mit Software ab, die die proprietären Schritte kennt - primär Huaweis eigene Health-App.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Kontrolle über das Ökosystem&lt;/strong&gt; - Nutzer werden in Huawei Health und seine Cloud-Dienste gezwungen. Später das Gerät oder die Plattform zu wechseln bedeutet, die eigene Gesundheitsdatenhistorie zu verlieren.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Weniger Nutzerwahl&lt;/strong&gt; - du willst eine Open-Source-App nutzen? Mehr Kontrolle über die Privatsphäre deiner Gesundheitsdaten? Pech gehabt - es sei denn, jemand reverse-engineered zuerst das Protokoll.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Und genau das ist der Punkt: &lt;strong&gt;Das ist nicht einzigartig für Huawei&lt;/strong&gt;. Das Forschungsprojekt WatchWitch &lt;small&gt;&lt;a href=&quot;#ref3&quot;&gt;[3]&lt;/a&gt;&lt;/small&gt; dokumentiert, wie alle großen Anbieter - Apple, Samsung, Xiaomi - proprietäre BLE-Protokolle nutzen, um Ökosystem-Lock-in durchzusetzen. Die Apple Watch ist &quot;incredibly tightly coupled with Apple&apos;s iPhone and iCloud ecosystem, using proprietary protocols that are unavailable to third parties.&quot; Es ist ein systemisches Branchenproblem.&lt;/p&gt;
&lt;p&gt;Aber Huaweis Implementierung ist besonders aggressiv. BLE &lt;em&gt;erlaubt&lt;/em&gt; eigene Services, klar. Aber den grundlegenden Authentifizierungsmechanismus durch einen proprietären Gatekeeper zu ersetzen, ist ein ganz anderes Spiel.&lt;/p&gt;
&lt;h2&gt;Die Sicherheitsironie&lt;/h2&gt;
&lt;p&gt;Die naheliegende Verteidigung lautet: &quot;Wir tun das aus Sicherheitsgründen.&quot; Schauen wir uns das an.&lt;/p&gt;
&lt;p&gt;Die BlueDoor-Schwachstellenforschung der Tsinghua University &lt;small&gt;&lt;a href=&quot;#ref4&quot;&gt;[4]&lt;/a&gt;&lt;/small&gt; testete 16 BLE-Geräte, darunter das Honor Band 3 (dasselbe Huawei-Ökosystem), und erreichte bei den meisten davon &lt;strong&gt;stilles Pairing ohne Nutzerautorisierung&lt;/strong&gt;. Das proprietäre Protokoll hat das nicht verhindert.&lt;/p&gt;
&lt;p&gt;Gleichzeitig wurde das Protokoll selbst mehrfach reverse-engineered - von der Gadgetbridge-Community, vom Projekt &lt;code class=&quot;language-text&quot;&gt;huawei-lpv2&lt;/code&gt;, von den Forschern, die auf der Easterhegg 2019 präsentierten &lt;small&gt;&lt;a href=&quot;#ref2&quot;&gt;[2]&lt;/a&gt;&lt;/small&gt;, und von mir für D2Explorer. Security through obscurity mit Ablaufdatum.&lt;/p&gt;
&lt;p&gt;Die HMAC-SHA256-Schlüsselableitung aus dem QR-Code ist tatsächlich ordentliche Kryptografie. Aber darum geht es nicht. Du könntest dieselben Sicherheitseigenschaften mit Standard-BLE Secure Connections und einer Out-of-Band-Pairing-Methode erreichen (wie NFC oder QR-Code) - ohne dabei jede Drittanbieter-Anwendung auszusperren.&lt;/p&gt;
&lt;h2&gt;Die Community schlägt zurück&lt;/h2&gt;
&lt;p&gt;Die Community hat das nicht still hingenommen.&lt;/p&gt;
&lt;h3&gt;Gadgetbridge&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://gadgetbridge.org/&quot;&gt;Gadgetbridge&lt;/a&gt; - die Open-Source-Android-App für Wearables - unterstützt inzwischen die Huawei Watch D2 &lt;small&gt;&lt;a href=&quot;#ref5&quot;&gt;[5]&lt;/a&gt;&lt;/small&gt;. Du kannst deine Uhr ohne Huaweis Health-App pairen. Es brauchte erheblichen Reverse-Engineering-Aufwand (siehe PR #2462 &lt;small&gt;&lt;a href=&quot;#ref6&quot;&gt;[6]&lt;/a&gt;&lt;/small&gt;), und es gibt Einschränkungen - die EKG-Funktion ist deaktiviert, wenn die Uhr mit Gadgetbridge gekoppelt ist &lt;small&gt;&lt;a href=&quot;#ref7&quot;&gt;[7]&lt;/a&gt;&lt;/small&gt; - aber es funktioniert.&lt;/p&gt;
&lt;p&gt;Die Authentifizierungsimplementierung in Gadgetbridge behandelt Auth-Version 3, berechnet den Bonding-Key aus der Pairing-Nachricht (Service &lt;code class=&quot;language-text&quot;&gt;0x01&lt;/code&gt;, Command &lt;code class=&quot;language-text&quot;&gt;0x0e&lt;/code&gt;) und nutzt ihn zur Entschlüsselung. Eine 17-stellige Huawei-Account-ID ist für die Aushandlung des Authentifizierungsschlüssels erforderlich.&lt;/p&gt;
&lt;h3&gt;huawei-lpv2&lt;/h3&gt;
&lt;p&gt;Das Projekt &lt;a href=&quot;https://github.com/zyv/huawei-lpv2&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;huawei-lpv2&lt;/code&gt;&lt;/a&gt; liefert eine reine Python-Implementierung von Huaweis Link Protocol v2 &lt;small&gt;&lt;a href=&quot;#ref8&quot;&gt;[8]&lt;/a&gt;&lt;/small&gt;. Es wird gepflegt, hat mehrere Forks und dient als Referenz für alle, die Integrationen für Huawei-Wearables außerhalb des offiziellen Ökosystems bauen.&lt;/p&gt;
&lt;h3&gt;D2Explorer&lt;/h3&gt;
&lt;p&gt;Mein eigenes D2Explorer-Projekt ging einen anderen Weg - eine C++-Implementierung mit SimpleBLE, die unter Linux und macOS funktioniert. Die Arbeit umfasste:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;TLV-Serialisierung/-Deserialisierung implementieren (&lt;code class=&quot;language-text&quot;&gt;HuaweiProtocol&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Präzise Message-Constructoren bauen (&lt;code class=&quot;language-text&quot;&gt;ProtocolMessageBuilder&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Die kryptografischen Schritte korrekt hinbekommen - Nonce-Erzeugung, HMAC-SHA256, XOR-Verschlüsselung (&lt;code class=&quot;language-text&quot;&gt;CryptoOperations&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;CryptoUtils&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Strikte Zustandsübergänge und Timing verwalten (&lt;code class=&quot;language-text&quot;&gt;HuaweiPairingProtocol&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;ProtocolStateManager&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Fehler debuggen, die durch Timing-Abweichungen im Millisekundenbereich und subtile Krypto-Fehler verursacht wurden.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;D2Explorer existiert &lt;em&gt;weil&lt;/em&gt; Huaweis Protokoll es nötig gemacht hat. Es ist der Workaround, der für grundlegende Funktionalität außerhalb des Walled Garden erforderlich ist.&lt;/p&gt;
&lt;h3&gt;AsteroidOS&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://asteroidos.org/&quot;&gt;AsteroidOS 2.0&lt;/a&gt; erschien im Februar 2026 als großes Update des Open-Source-Smartwatch-Betriebssystems auf Linux-Basis &lt;small&gt;&lt;a href=&quot;#ref9&quot;&gt;[9]&lt;/a&gt;&lt;/small&gt;. Es unterstützt nun etwa 30 Geräte, darunter die Huawei Watch und Huawei Watch 2, mit Funktionen wie Always-on-Display und Tilt-to-Wake. Eine vollständige Open-Source-Alternative zu Huaweis Firmware.&lt;/p&gt;
&lt;h2&gt;Die regulatorische Flut&lt;/h2&gt;
&lt;p&gt;Die EU schaut nicht nur zu. Der Digital Markets Act (DMA) beginnt, Veränderung zu erzwingen &lt;small&gt;&lt;a href=&quot;#ref10&quot;&gt;[10]&lt;/a&gt;&lt;/small&gt;.&lt;/p&gt;
&lt;p&gt;Im Dezember 2025 veröffentlichte Apple iOS 26.3 mit AirPods-ähnlichem Pairing für Drittanbieter-Geräte - darunter Huawei-Smartwatches - ausdrücklich, um die DMA-Anforderungen zu erfüllen &lt;small&gt;&lt;a href=&quot;#ref11&quot;&gt;[11]&lt;/a&gt;&lt;/small&gt;. Hintergrundsynchronisierung zwischen Huawei-Uhren und iPhones ist in Europa bereits in Betrieb.&lt;/p&gt;
&lt;p&gt;Der DMA schreibt vor, dass Gatekeeper Interoperabilität für verbundene Geräte bereitstellen. Das zielt direkt auf genau die Art proprietären BLE-Lock-ins, die Huawei (und Apple, und alle anderen) praktiziert haben. Der vollständige Rollout dieser Interoperabilitätsfunktionen wird im Laufe des Jahres 2026 erwartet.&lt;/p&gt;
&lt;p&gt;Das ist erheblich. Zum ersten Mal gibt es regulatorischen Druck, das zu standardisieren, was Anbieter absichtlich proprietär gehalten haben. Die technische Community kann Protokolle einzeln reverse-engineeren, aber Regulierung kann die Anreizstruktur für die ganze Branche verändern.&lt;/p&gt;
&lt;h2&gt;Was das bedeutet&lt;/h2&gt;
&lt;p&gt;Das Pairing-Protokoll der Huawei Watch D2 ist eine Fallstudie dafür, wie eigene Protokolle über Standard-Transportschichten Vendor Lock-in erzwingen können. Die Schichten aus proprietärer Kryptografie, eigenen Nachrichtenformaten und timingempfindlichen Handshakes existieren nicht, weil Standard-BLE keine Authentifizierung könnte - das kann es -, sondern weil proprietäre Protokolle Nutzer im Ökosystem halten.&lt;/p&gt;
&lt;p&gt;Das Bild verändert sich allerdings. Gadgetbridge gibt dir schon jetzt eine Alternative. Der EU DMA erzwingt Interoperabilität auf regulatorischer Ebene. Und Open-Source-Projekte wie &lt;code class=&quot;language-text&quot;&gt;huawei-lpv2&lt;/code&gt;, D2Explorer und AsteroidOS beweisen, dass die Community reverse-engineeren wird, was Anbieter einzuschließen versuchen.&lt;/p&gt;
&lt;p&gt;D2Explorer zu bauen hatte weniger mit Bluetooth zu tun und mehr mit kryptografischer Detektivarbeit. Es unterstreicht etwas, das eigentlich nicht unterstrichen werden sollte: Du solltest mit der Software deiner Wahl auf deine eigenen Gesundheitsdaten zugreifen können.&lt;/p&gt;
&lt;hr&gt;
&lt;h3&gt;Referenzen&lt;/h3&gt;
&lt;p&gt;&lt;a id=&quot;ref1&quot;&gt;&lt;/a&gt;1. &lt;a href=&quot;https://github.com/zyv/huawei-lpv2&quot;&gt;huawei-lpv2: Pure Python implementation of Huawei BLE Link Protocol v2&lt;/a&gt; - &lt;em&gt;Open-Source-Referenzimplementierung des Protokolls.&lt;/em&gt;&lt;br&gt;
&lt;a id=&quot;ref2&quot;&gt;&lt;/a&gt;2. &lt;a href=&quot;https://media.ccc.de/v/eh19-186-all-your-fitness-data-belongs-to-you-reverse-engineering-the-huawei-health-android-app&quot;&gt;All Your Fitness Data Belongs to You: Reverse Engineering the Huawei Health Android App&lt;/a&gt; - &lt;em&gt;Easterhegg-2019-Konferenzvortrag, der den Reverse-Engineering-Aufwand dokumentiert. &lt;a href=&quot;https://www.sba-research.org/wp-content/uploads/2019/04/easterhegg19.pdf&quot;&gt;Folien (PDF)&lt;/a&gt;.&lt;/em&gt;&lt;br&gt;
&lt;a id=&quot;ref3&quot;&gt;&lt;/a&gt;3. &lt;a href=&quot;https://arxiv.org/html/2507.07210v1&quot;&gt;WatchWitch: Academic Research on Smartwatch Interoperability&lt;/a&gt; - &lt;em&gt;Dokumentiert, wie alle großen Anbieter proprietäre Protokolle für Ökosystem-Lock-in nutzen.&lt;/em&gt;&lt;br&gt;
&lt;a id=&quot;ref4&quot;&gt;&lt;/a&gt;4. &lt;a href=&quot;https://tns.thss.tsinghua.edu.cn/~jiliang/publications/MOBISYS2020_BlueDoor.pdf&quot;&gt;BlueDoor: Breaking the Secure Information Flow via BLE Vulnerability (Tsinghua University)&lt;/a&gt; - &lt;em&gt;Fand stille Pairing-Schwachstellen in 16 BLE-Geräten, darunter Honor Band 3.&lt;/em&gt;&lt;br&gt;
&lt;a id=&quot;ref5&quot;&gt;&lt;/a&gt;5. &lt;a href=&quot;https://gadgetbridge.org/basics/topics/huawei-honor/&quot;&gt;Gadgetbridge: Huawei/Honor Device Support&lt;/a&gt; - &lt;em&gt;Offizielle Support-Seite für Huawei- und Honor-Wearables.&lt;/em&gt;&lt;br&gt;
&lt;a id=&quot;ref6&quot;&gt;&lt;/a&gt;6. &lt;a href=&quot;https://codeberg.org/Freeyourgadget/Gadgetbridge/pulls/2462&quot;&gt;Gadgetbridge PR #2462: Initial Huawei/Honor Support&lt;/a&gt; - &lt;em&gt;Der Pull Request, der Huawei-Geräteunterstützung zu Gadgetbridge hinzufügte.&lt;/em&gt;&lt;br&gt;
&lt;a id=&quot;ref7&quot;&gt;&lt;/a&gt;7. &lt;a href=&quot;https://codeberg.org/Freeyourgadget/Gadgetbridge/issues/4918&quot;&gt;Gadgetbridge Issue #4918: ECG Disabled with Gadgetbridge&lt;/a&gt; - &lt;em&gt;Bekannte Einschränkung bei der Nutzung von Gadgetbridge statt Huawei Health.&lt;/em&gt;&lt;br&gt;
&lt;a id=&quot;ref8&quot;&gt;&lt;/a&gt;8. &lt;a href=&quot;https://gadgetbridge.org/basics/pairing/huawei-honor-pairing/&quot;&gt;Gadgetbridge: Huawei/Honor Pairing Guide&lt;/a&gt; - &lt;em&gt;Schritt-für-Schritt-Pairing-Anleitung für Huawei-Geräte.&lt;/em&gt;&lt;br&gt;
&lt;a id=&quot;ref9&quot;&gt;&lt;/a&gt;9. &lt;a href=&quot;https://www.cnx-software.com/2026/02/18/asteroidos-2-0-open-source-smartwatch-os-released-now-supports-around-30-devices/&quot;&gt;AsteroidOS 2.0 Release&lt;/a&gt; - &lt;em&gt;Open-Source-Smartwatch-Betriebssystem, das nun etwa 30 Geräte unterstützt, darunter Huawei-Uhren.&lt;/em&gt;&lt;br&gt;
&lt;a id=&quot;ref10&quot;&gt;&lt;/a&gt;10. &lt;a href=&quot;https://digital-markets-act.ec.europa.eu/questions-and-answers/interoperability_en&quot;&gt;EU Digital Markets Act: Interoperability Requirements&lt;/a&gt; - &lt;em&gt;DMA-Bestimmungen, die Interoperabilität für verbundene Geräte vorschreiben.&lt;/em&gt;&lt;br&gt;
&lt;a id=&quot;ref11&quot;&gt;&lt;/a&gt;11. &lt;a href=&quot;https://www.macrumors.com/2025/12/22/ios-26-3-dma-airpods-pairing/&quot;&gt;iOS 26.3 DMA Features: Third-Party Smartwatch Pairing&lt;/a&gt; - &lt;em&gt;Apples Umsetzung der EU-Interoperabilitätsvorgaben für Wearables.&lt;/em&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h3&gt;Verwandte Beiträge&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;/bluez-pairing-python-agent-workaround-authentication-failed/&quot;&gt;BlueZ Pairing Fix: External Python Agent &amp;#x26; D-Bus Polling&lt;/a&gt; - der Vorläufer dieser Untersuchung. Bevor wir Huaweis proprietäres Protokoll angehen konnten, mussten wir BlueZs &lt;code class=&quot;language-text&quot;&gt;AuthenticationFailed&lt;/code&gt;-Fehler beim Standard-BLE-Pairing beheben.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/m1-mac-android-emulator-bluetooth-passthrough-bumble/&quot;&gt;Fix Android Emulator Bluetooth on M1 Mac using Bumble &amp;#x26; API 32&lt;/a&gt; - ein weiterer BLE-Integrationskampf, diesmal beim Durchreichen des physischen Bluetooth-Funks eines Macs in den Android Emulator.&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[BlueZ-Pairing-Fix: Externer Python-Agent & D-Bus-Polling]]></title><description><![CDATA[TL;DR: Wenn du mit einem eigenen C++/sd-bus-Pairing-Agent unter BlueZ 5.66+  bekommst, ist wahrscheinlich deine interne Agent-Registrierung…]]></description><link>https://bdteo.com/de/bluez-pairing-python-agent-workaround-authentication-failed/</link><guid isPermaLink="false">https://bdteo.com/de/bluez-pairing-python-agent-workaround-authentication-failed/</guid><pubDate>Tue, 08 Apr 2025 00:00:00 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;TL;DR:&lt;/strong&gt; Wenn du mit einem eigenen C++/sd-bus-Pairing-Agent unter BlueZ 5.66+ &lt;code class=&quot;language-text&quot;&gt;org.bluez.Error.AuthenticationFailed&lt;/code&gt; bekommst, ist wahrscheinlich deine interne Agent-Registrierung das Problem. Starte einen externen Python-Agent (&lt;code class=&quot;language-text&quot;&gt;simple-agent.py&lt;/code&gt;) als separaten Prozess und implementiere D-Bus-Property-Polling, statt dich auf &lt;code class=&quot;language-text&quot;&gt;PropertiesChanged&lt;/code&gt;-Signale zu verlassen. Details und Code unten.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Ich habe zwei Tage lang auf &lt;code class=&quot;language-text&quot;&gt;org.bluez.Error.AuthenticationFailed&lt;/code&gt; gestarrt, bevor ich verstanden habe, was eigentlich los war.&lt;/p&gt;
&lt;p&gt;Der Pairing-Agent war registriert. Die D-Bus-Aufrufe sahen korrekt aus. &lt;code class=&quot;language-text&quot;&gt;busctl&lt;/code&gt; bestätigte, dass alles an Ort und Stelle war -- und BlueZ sagte einfach weiter nein. Das passierte während der Arbeit an &lt;a href=&quot;../huawei-watch-d2-proprietary-protocol-vendor-lockin/&quot;&gt;D2Explorer&lt;/a&gt; -- einem Tool zum Pairing mit der Huawei Watch D2 unter Linux -- und der Pairing-Fehler blockierte alles.&lt;/p&gt;
&lt;p&gt;Das ist wirklich passiert, und so haben wir es behoben.&lt;/p&gt;
&lt;h2&gt;Der Plan: ein interner C++-Pairing-Agent&lt;/h2&gt;
&lt;p&gt;Die Idee war sauber und in sich geschlossen. Eine einzige C++-Anwendung, die den gesamten Pairing-Prozess mit &lt;code class=&quot;language-text&quot;&gt;sd-bus&lt;/code&gt; übernimmt (den C/C++-D-Bus-Bindings):&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Mit dem System-D-Bus verbinden.&lt;/li&gt;
&lt;li&gt;Den Bluetooth-Adapter finden (&lt;code class=&quot;language-text&quot;&gt;org.bluez.Adapter1&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Eine C++-Klasse implementieren, die das Interface &lt;code class=&quot;language-text&quot;&gt;org.bluez.Agent1&lt;/code&gt; bereitstellt.&lt;/li&gt;
&lt;li&gt;Den Agent bei &lt;code class=&quot;language-text&quot;&gt;org.bluez.AgentManager1&lt;/code&gt; über &lt;code class=&quot;language-text&quot;&gt;RegisterAgent&lt;/code&gt; und &lt;code class=&quot;language-text&quot;&gt;RequestDefaultAgent&lt;/code&gt; registrieren. Wir begannen mit der Capability &lt;code class=&quot;language-text&quot;&gt;DisplayYesNo&lt;/code&gt;, später vereinfachten wir auf &lt;code class=&quot;language-text&quot;&gt;NoInputNoOutput&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Das Zielgerät entdecken (&lt;code class=&quot;language-text&quot;&gt;org.bluez.Device1&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;Pair()&lt;/code&gt; auf dem D-Bus-Interface des Geräts aufrufen.&lt;/li&gt;
&lt;li&gt;Der interne Agent behandelt Callbacks (&lt;code class=&quot;language-text&quot;&gt;RequestConfirmation&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;RequestAuthorization&lt;/code&gt;) automatisch -- keine Benutzerinteraktion nötig.&lt;/li&gt;
&lt;li&gt;Das Gerät als vertrauenswürdig markieren, eine GATT-Verbindung aufbauen, fertig.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Ein Binary, keine externen Abhängigkeiten. Das war der Plan.&lt;/p&gt;
&lt;h2&gt;Die Wand: &lt;code class=&quot;language-text&quot;&gt;org.bluez.Error.AuthenticationFailed&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;Bis Schritt 6 funktionierte alles. Adapter gefunden, Agent registriert (D-Bus bestätigte es), Gerät entdeckt. Aber in dem Moment, in dem wir &lt;code class=&quot;language-text&quot;&gt;Device1.Pair()&lt;/code&gt; über &lt;code class=&quot;language-text&quot;&gt;sd_bus_call_method&lt;/code&gt; aufriefen -- sofortiger Fehlschlag:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;[BluetoothDevice] Calling Device1.Pair() method via D-Bus
[BluetoothDevice] Device1.Pair() method threw exception: Failed to call method &apos;Pair&apos;:
    Input/output error - D-Bus error: org.bluez.Error.AuthenticationFailed (Authentication Failed)&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Wir haben alles versucht. Verschiedene Agent-Capabilities. Die &lt;code class=&quot;language-text&quot;&gt;sd-bus&lt;/code&gt;-vtable-Konfiguration geprüft. Verifiziert, dass die Agent-Methodenimplementierungen schnell erfolgreich zurückgaben. Mit &lt;code class=&quot;language-text&quot;&gt;busctl&lt;/code&gt; und &lt;code class=&quot;language-text&quot;&gt;gdbus&lt;/code&gt; den D-Bus-Traffic beobachtet -- die Registrierungsaufrufe sahen korrekt aus. Der &lt;code class=&quot;language-text&quot;&gt;Pair()&lt;/code&gt;-Aufruf scheiterte einfach weiter.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Sackgasse.&lt;/strong&gt;&lt;/p&gt;
&lt;h2&gt;Der Durchbruch: ein externer Python-Agent&lt;/h2&gt;
&lt;p&gt;Um das Problem zu isolieren, nahmen wir den internen C++-Agent aus der Gleichung. Wir starteten BlueZs Standard-&lt;code class=&quot;language-text&quot;&gt;simple-agent.py&lt;/code&gt; als separaten Prozess &lt;em&gt;bevor&lt;/em&gt; wir unsere C++-App starteten (jetzt ohne eigene Agent-Registrierung):&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# Terminal 1: Run the external agent&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; python simple-agent.py NoInputNoOutput

&lt;span class=&quot;token comment&quot;&gt;# Terminal 2: Run our C++ app (no internal agent)&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; ./build/huawei_pair_app &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;MAC&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;QR_VALUE&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Das Ergebnis:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;[BluetoothDevice] Calling Device1.Pair() method via D-Bus
[BluetoothDevice] Device1.Pair() method succeeded  &amp;lt;--- SUCCESS!&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Konstant. Jedes Mal. Der Fehler &lt;code class=&quot;language-text&quot;&gt;AuthenticationFailed&lt;/code&gt; verschwand komplett.&lt;/p&gt;
&lt;p&gt;Das bewies, dass das Problem nicht bei &lt;code class=&quot;language-text&quot;&gt;Pair()&lt;/code&gt; selbst lag, und auch nicht beim Gerät oder bei BlueZs Pairing-Fähigkeit. Es lag konkret daran, wie unsere C++-Anwendung mit &lt;code class=&quot;language-text&quot;&gt;sd-bus&lt;/code&gt; sich als Pairing-Agent registrierte und interagierte. Die exakt gleiche logische Operation -- einen &lt;code class=&quot;language-text&quot;&gt;NoInputNoOutput&lt;/code&gt;-Agent registrieren und &lt;code class=&quot;language-text&quot;&gt;Pair()&lt;/code&gt; aufrufen -- funktionierte perfekt, wenn der Agent als separater Python-Prozess lief.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Das funktionierte.&lt;/strong&gt;&lt;/p&gt;
&lt;h2&gt;Warum ist der interne Agent gescheitert?&lt;/h2&gt;
&lt;p&gt;Als ich zuerst darauf stieß, hatte ich nur Hypothesen. Seitdem habe ich echte dokumentierte Hinweise gefunden, dass das ein breiteres Problem ist -- nicht nur unser Code.&lt;/p&gt;
&lt;h3&gt;BlueZ-5.70+-Regression&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/bluez/bluez/issues/605&quot;&gt;BlueZ GitHub Issue #605&lt;/a&gt; dokumentiert Fälle, in denen Geräte unter BlueZ 5.50 problemlos pairen, auf neueren Versionen aber mit &lt;code class=&quot;language-text&quot;&gt;auth failed with status 0x05&lt;/code&gt; scheitern. HCI-Logs zeigen &lt;code class=&quot;language-text&quot;&gt;Status: PIN or Key Missing (0x06)&lt;/code&gt; trotz gespeicherter Link Keys. Der Workaround? Das alte &lt;code class=&quot;language-text&quot;&gt;bluez-simple-agent.py&lt;/code&gt;-Skript ausführen. Kommt dir bekannt vor?&lt;/p&gt;
&lt;h3&gt;Agent-Verfügbarkeit ist die eigentliche Ursache&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/hbldh/bleak/issues/1434&quot;&gt;Bleak Issue #1434&lt;/a&gt; macht es noch klarer: Pairing funktioniert nur, wenn &lt;code class=&quot;language-text&quot;&gt;bluetoothctl&lt;/code&gt; oder GNOME Bluetooth läuft, weil diese Anwendungen den nötigen Authentication-Agent registrieren. Ohne einen aktiven, &lt;em&gt;korrekt funktionierenden&lt;/em&gt; Agent gibt BlueZ intern &lt;code class=&quot;language-text&quot;&gt;No agent available for request type 2&lt;/code&gt; zurück -- was nach außen als &lt;code class=&quot;language-text&quot;&gt;AuthenticationFailed&lt;/code&gt; erscheint.&lt;/p&gt;
&lt;p&gt;Die zentrale Erkenntnis: Es reicht nicht, einen Agent zu &lt;em&gt;registrieren&lt;/em&gt;. Der Agent muss auf BlueZs Callbacks auf eine Weise antworten, die &lt;code class=&quot;language-text&quot;&gt;bluetoothd&lt;/code&gt; als gültig betrachtet. Und irgendetwas daran, wie &lt;code class=&quot;language-text&quot;&gt;sd-bus&lt;/code&gt; das innerhalb desselben Prozesses handhabt, der das Pairing startet, reicht neueren BlueZ-Versionen nicht.&lt;/p&gt;
&lt;h3&gt;Vielleicht ist es nicht einmal BlueZ&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://bugzilla.redhat.com/show_bug.cgi?id=1905671&quot;&gt;Red Hat Bug #1905671&lt;/a&gt; zeigte, dass manche &lt;code class=&quot;language-text&quot;&gt;AuthenticationFailed&lt;/code&gt;-Fehler kernelbezogen sind, nicht BlueZ-bezogen. Kernel 5.9 hatte Pairing-Probleme, die 5.8.18 und 5.10+ nicht hatten. Der Kommentar des Maintainers ist zitierenswert: &lt;em&gt;&quot;Bluetooth is complex, it could be firmware, kernel, bluez, controller, end device or a combination of them all.&quot;&lt;/em&gt;&lt;/p&gt;
&lt;h3&gt;Agent-Capability-Mismatch&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/bluez/bluez/issues/650&quot;&gt;BlueZ Issue #650&lt;/a&gt; dokumentiert noch einen anderen Winkel: Bestimmte Geräte (besonders iOS) scheitern beim Pairing mit &lt;code class=&quot;language-text&quot;&gt;NoInputNoOutput&lt;/code&gt;-Agents, weil sie Secure Connections auf Legacy Pairing herabstufen, was bei späterem Attribute-Zugriff &lt;code class=&quot;language-text&quot;&gt;Insufficient Authentication (0x05)&lt;/code&gt;-Fehler verursacht. Das ist ein Security-Manager-Protocol-(SMP)-Negotiation-Problem, kein Problem der Agent-Registrierung -- aber es erzeugt dieselbe Fehlermeldung.&lt;/p&gt;
&lt;h3&gt;Die wahrscheinlichsten Ursachen in unserem Fall&lt;/h3&gt;
&lt;p&gt;Angesichts der Hinweise sind das die wahrscheinlichsten Erklärungen für den internen &lt;code class=&quot;language-text&quot;&gt;sd-bus&lt;/code&gt;-Agent-Fehlschlag:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Timing&lt;/strong&gt; -- die &lt;code class=&quot;language-text&quot;&gt;sd-bus&lt;/code&gt;-Registrierung oder Methodenbehandlung innerhalb unserer Event Loop antwortete nicht genau in dem Zeitfenster, das &lt;code class=&quot;language-text&quot;&gt;bluetoothd&lt;/code&gt; erwartete.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Feinheiten von &lt;code class=&quot;language-text&quot;&gt;sd-bus&lt;/code&gt; vs. &lt;code class=&quot;language-text&quot;&gt;python-dbus&lt;/code&gt;&lt;/strong&gt; -- Unterschiede darin, wie diese Libraries mit dem D-Bus-Daemon interagieren oder Object Lifetimes behandeln.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Strengere Anforderungen in BlueZ 5.66+&lt;/strong&gt; -- geänderte interne Sequenzen für Agent-Interaktion, die &lt;code class=&quot;language-text&quot;&gt;sd-bus&lt;/code&gt;, wenn es in derselben Anwendung verwendet wird, die das Pairing startet, nicht erfüllt.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;Die zweite Wand: D-Bus-Signale sind unzuverlässig&lt;/h2&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;AuthenticationFailed&lt;/code&gt; zu überwinden war ein großer Gewinn, aber es war nicht das Ende. Mit dem externen Agent an Ort und Stelle war &lt;code class=&quot;language-text&quot;&gt;Pair()&lt;/code&gt; erfolgreich -- aber wir konnten nicht zuverlässig &lt;em&gt;erkennen&lt;/em&gt;, wann es fertig war.&lt;/p&gt;
&lt;p&gt;Wir verließen uns auf D-Bus-&lt;code class=&quot;language-text&quot;&gt;PropertiesChanged&lt;/code&gt;-Signale (über &lt;code class=&quot;language-text&quot;&gt;sd-bus&lt;/code&gt;), um zu wissen, wann &lt;code class=&quot;language-text&quot;&gt;Paired&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;Trusted&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;Connected&lt;/code&gt; und &lt;code class=&quot;language-text&quot;&gt;ServicesResolved&lt;/code&gt; auf &lt;code class=&quot;language-text&quot;&gt;true&lt;/code&gt; wechselten. Manchmal kamen die Signale an. Manchmal kamen sie spät. Manchmal kamen sie gar nicht.&lt;/p&gt;
&lt;p&gt;Also implementierten wir &lt;strong&gt;aktives Polling&lt;/strong&gt; -- einen Fallback, der Property-Werte direkt abfragt, wenn Signale nicht auftauchen:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;cpp&quot;&gt;&lt;pre class=&quot;language-cpp&quot;&gt;&lt;code class=&quot;language-cpp&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;BluetoothDevice&lt;/span&gt;&lt;span class=&quot;token double-colon punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;isPaired&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;bool&lt;/span&gt; cachedValue &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; mockPaired_&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;load&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// Check signal-updated cache&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;cachedValue&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;// Signal didn&apos;t fire? Poll D-Bus directly.&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;Logger&lt;/span&gt;&lt;span class=&quot;token double-colon punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;debug&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;[Polling] Polling Paired property via D-Bus...&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;bool&lt;/span&gt; polledValue &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    adapter_&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token generic-function&quot;&gt;&lt;span class=&quot;token function&quot;&gt;getObjectProperty&lt;/span&gt;&lt;span class=&quot;token generic class-name&quot;&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;bool&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
        devicePath_&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;org.bluez.Device1&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Paired&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; polledValue
    &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;polledValue&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; mockPaired_&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;store&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// Update cache&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; polledValue&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Jede State-Transition-Methode (&lt;code class=&quot;language-text&quot;&gt;isPaired()&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;isTrusted()&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;isConnected()&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;areServicesResolved()&lt;/code&gt;) folgt demselben Muster: zuerst den gecachten atomic boolean prüfen (vom Signal-Handler aktualisiert, wenn er funktioniert), dann auf einen direkten D-Bus-&lt;code class=&quot;language-text&quot;&gt;Get&lt;/code&gt;-Property-Aufruf zurückfallen.&lt;/p&gt;
&lt;p&gt;Nicht elegant. Aber nötig.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Das funktionierte.&lt;/strong&gt;&lt;/p&gt;
&lt;h2&gt;Der komplette Fix&lt;/h2&gt;
&lt;p&gt;Hier ist das konsolidierte Rezept. Wenn du automatisiertes Bluetooth-Pairing unter Linux mit BlueZ 5.66+ baust und auf &lt;code class=&quot;language-text&quot;&gt;AuthenticationFailed&lt;/code&gt; läufst:&lt;/p&gt;
&lt;h3&gt;Schritt 1: simple-agent.py holen&lt;/h3&gt;
&lt;p&gt;Hol es aus dem &lt;a href=&quot;https://git.kernel.org/pub/scm/bluetooth/bluez.git/tree/test/simple-agent&quot;&gt;BlueZ source tree&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;Schritt 2: den externen Agent starten&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; python simple-agent.py NoInputNoOutput&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Lass das in einem separaten Terminal laufen (oder als Background Service).&lt;/p&gt;
&lt;h3&gt;Schritt 3: den internen Agent aus deiner App entfernen&lt;/h3&gt;
&lt;p&gt;Entferne alle &lt;code class=&quot;language-text&quot;&gt;RegisterAgent&lt;/code&gt;- / &lt;code class=&quot;language-text&quot;&gt;RequestDefaultAgent&lt;/code&gt;-Aufrufe aus deiner C++-Anwendung. Lass den externen Python-Agent die Authentication-Callbacks behandeln.&lt;/p&gt;
&lt;h3&gt;Schritt 4: D-Bus-Property-Polling hinzufügen&lt;/h3&gt;
&lt;p&gt;Verlass dich nicht nur auf &lt;code class=&quot;language-text&quot;&gt;PropertiesChanged&lt;/code&gt;-Signale. Implementiere für jede kritische Property (&lt;code class=&quot;language-text&quot;&gt;Paired&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;Trusted&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;Connected&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;ServicesResolved&lt;/code&gt;) das oben gezeigte Cache-then-poll-Muster. Polle periodisch aus deiner Main Loop.&lt;/p&gt;
&lt;h3&gt;Schritt 5: verifizieren&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;Bestätige, dass der externe Agent läuft (&lt;code class=&quot;language-text&quot;&gt;sudo python simple-agent.py NoInputNoOutput&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Starte deine App. &lt;code class=&quot;language-text&quot;&gt;Pair()&lt;/code&gt; sollte erfolgreich sein.&lt;/li&gt;
&lt;li&gt;Beobachte die Polling-Logs -- du solltest D-Bus-Property-Abfragen für State Transitions sehen.&lt;/li&gt;
&lt;li&gt;Wenn &lt;code class=&quot;language-text&quot;&gt;Pair()&lt;/code&gt; immer noch scheitert, prüfe deine BlueZ-Version (&lt;code class=&quot;language-text&quot;&gt;bluetoothd --version&lt;/code&gt;) und Kernel-Version -- das Problem könnte tiefer liegen.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;Was dich das kostet&lt;/h2&gt;
&lt;p&gt;Ich werde nicht so tun, als wäre das eine saubere Lösung. Ist es nicht:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Externe Abhängigkeit&lt;/strong&gt; -- deine App braucht jetzt einen separaten laufenden Python-Prozess.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Mehr Komplexität&lt;/strong&gt; -- Polling-Logik in der Main Loop, zusätzlich zu Signal-Handlern.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Weniger in sich geschlossen&lt;/strong&gt; -- der Traum vom einzelnen Binary ist dahin.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Aber es funktioniert. Zuverlässig. Und wenn du zwei Tage lang auf &lt;code class=&quot;language-text&quot;&gt;AuthenticationFailed&lt;/code&gt; gestarrt hast, ist &quot;es funktioniert&quot; das, was zählt.&lt;/p&gt;
&lt;hr&gt;
&lt;h3&gt;Referenzen&lt;/h3&gt;
&lt;p&gt;&lt;a id=&quot;ref1&quot;&gt;&lt;/a&gt;1. &lt;a href=&quot;https://github.com/bluez/bluez/issues/55&quot;&gt;BlueZ GitHub Issue #55: Device characteristics and pairing timing&lt;/a&gt; -- &lt;em&gt;Intermittent pairing failures related to agent timing.&lt;/em&gt;&lt;br&gt;
&lt;a id=&quot;ref2&quot;&gt;&lt;/a&gt;2. &lt;a href=&quot;https://forums.raspberrypi.com/viewtopic.php?t=324225&quot;&gt;Bluetooth Auto Pairing with NoInputNoOutput Agent Issues&lt;/a&gt; -- &lt;em&gt;Forum discussion about headless pairing challenges.&lt;/em&gt;&lt;br&gt;
&lt;a id=&quot;ref3&quot;&gt;&lt;/a&gt;3. &lt;a href=&quot;https://git.kernel.org/pub/scm/bluetooth/bluez.git/tree/test/simple-agent&quot;&gt;BlueZ Source: test/simple-agent&lt;/a&gt; -- &lt;em&gt;The standard Python agent.&lt;/em&gt;&lt;br&gt;
&lt;a id=&quot;ref4&quot;&gt;&lt;/a&gt;4. &lt;a href=&quot;https://github.com/bluez/bluez/issues/605&quot;&gt;BlueZ GitHub Issue #605: Pairing regression in 5.70+&lt;/a&gt; -- &lt;em&gt;Documented failures with newer BlueZ versions.&lt;/em&gt;&lt;br&gt;
&lt;a id=&quot;ref5&quot;&gt;&lt;/a&gt;5. &lt;a href=&quot;https://github.com/hbldh/bleak/issues/1434&quot;&gt;Bleak Issue #1434: Pairing requires active agent&lt;/a&gt; -- &lt;em&gt;Evidence that agent availability is the root cause.&lt;/em&gt;&lt;br&gt;
&lt;a id=&quot;ref6&quot;&gt;&lt;/a&gt;6. &lt;a href=&quot;https://bugzilla.redhat.com/show_bug.cgi?id=1905671&quot;&gt;Red Hat Bug #1905671: Kernel-related pairing failures&lt;/a&gt; -- &lt;em&gt;Not always BlueZ -- sometimes it&apos;s the kernel.&lt;/em&gt;&lt;br&gt;
&lt;a id=&quot;ref7&quot;&gt;&lt;/a&gt;7. &lt;a href=&quot;https://github.com/bluez/bluez/issues/650&quot;&gt;BlueZ GitHub Issue #650: Agent capability mismatch&lt;/a&gt; -- &lt;em&gt;SMP negotiation failures with NoInputNoOutput.&lt;/em&gt;&lt;br&gt;
&lt;a id=&quot;ref8&quot;&gt;&lt;/a&gt;8. &lt;a href=&quot;https://bluez.readthedocs.io/en/latest/agent-api/&quot;&gt;BlueZ Agent API Documentation&lt;/a&gt; -- &lt;em&gt;Official agent interface reference.&lt;/em&gt;&lt;br&gt;
&lt;a id=&quot;ref9&quot;&gt;&lt;/a&gt;9. &lt;a href=&quot;https://technotes.kynetics.com/2018/pairing_agents_bluez/&quot;&gt;Kynetics: Pairing Agents in the BlueZ Stack&lt;/a&gt; -- &lt;em&gt;Technical deep dive into agent registration.&lt;/em&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h3&gt;Ähnliche Beiträge&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;/huawei-watch-d2-proprietary-protocol-vendor-lockin/&quot;&gt;Huawei Watch D2 BLE Pairing: Protocol &amp;#x26; Vendor Lock-In&lt;/a&gt; -- das Projekt, das diese Untersuchung ausgelöst hat. Die Watch D2 verlangt zusätzlich zum Standard-BLE-Pairing einen proprietären Handshake auf Anwendungsebene; genau deshalb musste automatisiertes Pairing überhaupt funktionieren.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/m1-mac-android-emulator-bluetooth-passthrough-bumble/&quot;&gt;Fix Android Emulator Bluetooth on M1 Mac using Bumble &amp;#x26; API 32&lt;/a&gt; -- ein weiterer Bluetooth-Integrationskampf, diesmal beim Durchreichen des physischen Funkmoduls eines Macs in den Android Emulator.&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[Entwicklerleitfaden: Klassen- und Namespace-Updates in Shopware 6.5/6.6]]></title><description><![CDATA[Shopware 6.5 und 6.6 haben mehrere wichtige Änderungen an Klassen, Namespaces, Data-Attributen und Sicherheitsmechanismen eingeführt, die…]]></description><link>https://bdteo.com/de/understanding-class-namespace-changes-shopware-6-5-developers-guide/</link><guid isPermaLink="false">https://bdteo.com/de/understanding-class-namespace-changes-shopware-6-5-developers-guide/</guid><pubDate>Sat, 30 Nov 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Shopware 6.5 und 6.6 haben mehrere wichtige Änderungen an Klassen, Namespaces, Data-Attributen und Sicherheitsmechanismen eingeführt, die Entwickler kennen müssen, wenn sie ihre Shopware-Projekte aktualisieren oder warten. Dieser Artikel gibt einen knappen, aber umfassenden Überblick über diese Änderungen, zusammen mit Beobachtungen zu ihren Auswirkungen und dazu, wie du deinen Code entsprechend anpasst.&lt;/p&gt;
&lt;h2&gt;Einführung&lt;/h2&gt;
&lt;p&gt;Wenn Shopware sich weiterentwickelt, bringen Updates oft Verbesserungen, Optimierungen und neue Funktionen. Sie können aber auch Änderungen einführen, die bestehende Codebasen betreffen. Diese Änderungen zu verstehen, ist entscheidend, um einen reibungslosen Übergang sicherzustellen und die neuen Möglichkeiten sinnvoll zu nutzen.&lt;/p&gt;
&lt;p&gt;Dieser Artikel konzentriert sich auf die folgenden Kernbereiche:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Elasticsearch-Namespace-Migration&lt;/li&gt;
&lt;li&gt;Aktualisierungen beim Media-Path-Handling&lt;/li&gt;
&lt;li&gt;Methodenänderungen in &lt;code class=&quot;language-text&quot;&gt;AvailableCombinationLoader&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Symfony-Framework-Upgrade auf Version 6&lt;/li&gt;
&lt;li&gt;Aktualisierungen beim Stock-Handling&lt;/li&gt;
&lt;li&gt;Storefront-Bootstrap-Upgrade&lt;/li&gt;
&lt;li&gt;Änderungen am Data-Attribut des Offcanvas Cart&lt;/li&gt;
&lt;li&gt;Änderungen beim CSRF-Schutz&lt;/li&gt;
&lt;li&gt;Erweiterungen am Rule Builder&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Schauen wir uns jede dieser Änderungen im Detail an.&lt;/p&gt;
&lt;h2&gt;1. Elasticsearch-Namespace-Migration&lt;/h2&gt;
&lt;h3&gt;Überblick der Änderung&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Vorheriger Namespace:&lt;/strong&gt; &lt;code class=&quot;language-text&quot;&gt;ONGR\ElasticsearchDSL&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Neuer Namespace:&lt;/strong&gt; &lt;code class=&quot;language-text&quot;&gt;OpenSearchDSL&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Auswirkung&lt;/h3&gt;
&lt;p&gt;Alle Klassen und Methoden, die mit Elasticsearch interagieren, müssen ihre Namespaces aktualisieren, um die Migration von &lt;code class=&quot;language-text&quot;&gt;ONGR\ElasticsearchDSL&lt;/code&gt; zu &lt;code class=&quot;language-text&quot;&gt;OpenSearchDSL&lt;/code&gt; abzubilden.&lt;/p&gt;
&lt;h3&gt;Erforderliche Aktion&lt;/h3&gt;
&lt;p&gt;Aktualisiere Import-Statements und Referenzen in deinem Code, sodass sie den neuen Namespace verwenden.&lt;/p&gt;
&lt;h3&gt;Beispiel&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;php&quot;&gt;&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-php&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Before&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;token package&quot;&gt;ONGR&lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;ElasticsearchDSL&lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;Query&lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;MatchQuery&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token variable&quot;&gt;$query&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MatchQuery&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&apos;field&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string single-quoted-string&quot;&gt;&apos;value&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// After&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;token package&quot;&gt;OpenSearchDSL&lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;Query&lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;MatchQuery&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token variable&quot;&gt;$query&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MatchQuery&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&apos;field&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string single-quoted-string&quot;&gt;&apos;value&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3&gt;Beobachtungen&lt;/h3&gt;
&lt;p&gt;Diese Änderung passt zur breiteren Verschiebung der Branche hin zu OpenSearch, einem communitygetriebenen Fork von Elasticsearch. Die Aktualisierung auf den neuen Namespace stellt Kompatibilität mit laufender Entwicklung und Unterstützung sicher.&lt;/p&gt;
&lt;h2&gt;2. Media-Path-Handling&lt;/h2&gt;
&lt;h3&gt;Überblick der Änderung&lt;/h3&gt;
&lt;p&gt;Media-Pfade werden nun direkt in der Datenbank gespeichert, statt dynamisch generiert zu werden.&lt;/p&gt;
&lt;h3&gt;Auswirkung&lt;/h3&gt;
&lt;p&gt;Klassen und Services, die bisher auf dynamische Pfadgenerierung angewiesen waren, müssen Media-Pfade nun aus der Datenbank abrufen.&lt;/p&gt;
&lt;h3&gt;Erforderliche Aktion&lt;/h3&gt;
&lt;p&gt;Passe deinen Code so an, dass er die Methode &lt;code class=&quot;language-text&quot;&gt;getPath()&lt;/code&gt; der &lt;code class=&quot;language-text&quot;&gt;MediaEntity&lt;/code&gt; verwendet.&lt;/p&gt;
&lt;h3&gt;Beispiel&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;php&quot;&gt;&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-php&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Before (dynamic path generation)&lt;/span&gt;
&lt;span class=&quot;token variable&quot;&gt;$mediaPath&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$mediaService&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getPath&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$mediaId&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// After (database-stored path)&lt;/span&gt;
&lt;span class=&quot;token variable&quot;&gt;$mediaPath&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$mediaEntity&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getPath&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3&gt;Beobachtungen&lt;/h3&gt;
&lt;p&gt;Media-Pfade in der Datenbank zu speichern verbessert die Performance, weil weniger Rechenaufwand entsteht. Außerdem sorgt es für mehr Konsistenz und Zuverlässigkeit im Umgang mit Medien.&lt;/p&gt;
&lt;h2&gt;3. Methoden-Update in &lt;code class=&quot;language-text&quot;&gt;AvailableCombinationLoader&lt;/code&gt;&lt;/h2&gt;
&lt;h3&gt;Überblick der Änderung&lt;/h3&gt;
&lt;p&gt;Die Methode &lt;code class=&quot;language-text&quot;&gt;load()&lt;/code&gt; in &lt;code class=&quot;language-text&quot;&gt;AbstractAvailableCombinationLoader&lt;/code&gt; wurde durch &lt;code class=&quot;language-text&quot;&gt;loadCombinations()&lt;/code&gt; ersetzt.&lt;/p&gt;
&lt;h3&gt;Auswirkung&lt;/h3&gt;
&lt;p&gt;Alle eigenen Klassen, die &lt;code class=&quot;language-text&quot;&gt;AbstractAvailableCombinationLoader&lt;/code&gt; erweitern, müssen die neue Methode &lt;code class=&quot;language-text&quot;&gt;loadCombinations()&lt;/code&gt; statt der alten Methode &lt;code class=&quot;language-text&quot;&gt;load()&lt;/code&gt; implementieren.&lt;/p&gt;
&lt;h3&gt;Erforderliche Aktion&lt;/h3&gt;
&lt;p&gt;Benenne deine Methodenimplementierungen um oder refaktorisiere sie, damit sie zum neuen Methodennamen und zur neuen Signatur passen.&lt;/p&gt;
&lt;h3&gt;Beispiel&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;php&quot;&gt;&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-php&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Before&lt;/span&gt;
&lt;span class=&quot;token variable&quot;&gt;$combinations&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$combinationLoader&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;load&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$productId&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// After&lt;/span&gt;
&lt;span class=&quot;token variable&quot;&gt;$combinations&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$combinationLoader&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;loadCombinations&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$productId&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3&gt;Beobachtungen&lt;/h3&gt;
&lt;p&gt;Diese Änderung erhöht die Klarheit, weil der Methodenname beschreibender ist. Sie kann außerdem zusätzliche Parameter oder Rückgabetypen betreffen, daher ist es wichtig, die Methodensignatur zu prüfen.&lt;/p&gt;
&lt;h2&gt;4. Symfony-Framework-Upgrade auf Version 6&lt;/h2&gt;
&lt;h3&gt;Überblick der Änderung&lt;/h3&gt;
&lt;p&gt;Shopware hat seine Symfony-Komponenten auf Version 6 aktualisiert.&lt;/p&gt;
&lt;h3&gt;Auswirkung&lt;/h3&gt;
&lt;p&gt;Das Upgrade führt einige Breaking Changes ein, bedingt durch entfernte veraltete Funktionen und Änderungen an Methodensignaturen. Eigener Code, der auf älteren Symfony-Funktionen basiert, kann brechen oder Warnungen erzeugen.&lt;/p&gt;
&lt;h3&gt;Erforderliche Aktion&lt;/h3&gt;
&lt;p&gt;Prüfe deinen Code auf veraltete Symfony-Funktionen und aktualisiere ihn so, dass er mit Symfony 6 kompatibel ist.&lt;/p&gt;
&lt;h3&gt;Beobachtungen&lt;/h3&gt;
&lt;p&gt;Mit der aktuellen Symfony-Version Schritt zu halten sorgt für bessere Performance, Sicherheit und Zugriff auf neue Funktionen. Gleichzeitig verlangt es sorgfältige Codeprüfung und Tests, um Kompatibilität sicherzustellen.&lt;/p&gt;
&lt;h2&gt;5. Aktualisierungen beim Stock-Handling&lt;/h2&gt;
&lt;h3&gt;Überblick der Änderung&lt;/h3&gt;
&lt;p&gt;Eine neue Stock API wurde eingeführt, verfügbar hinter dem Feature-Flag &lt;code class=&quot;language-text&quot;&gt;STOCK_HANDLING&lt;/code&gt;.&lt;/p&gt;
&lt;h3&gt;Auswirkung&lt;/h3&gt;
&lt;p&gt;Klassen und Services rund um Bestandsverwaltung müssen sich möglicherweise an die neue API-Struktur anpassen, besonders wenn sie direkt mit Bestandsdaten arbeiten.&lt;/p&gt;
&lt;h3&gt;Erforderliche Aktion&lt;/h3&gt;
&lt;p&gt;Verwende die neuen Methoden zur Bestandsverarbeitung, die die API bereitstellt, und stelle sicher, dass bestandsbezogene Logik zur aktualisierten Struktur passt.&lt;/p&gt;
&lt;h3&gt;Beispiel&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;php&quot;&gt;&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-php&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Before&lt;/span&gt;
&lt;span class=&quot;token variable&quot;&gt;$stock&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$productEntity&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getStock&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// After&lt;/span&gt;
&lt;span class=&quot;token variable&quot;&gt;$stock&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$stockService&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getStock&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$productId&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3&gt;Beobachtungen&lt;/h3&gt;
&lt;p&gt;Die neue Stock API bietet eine robustere und flexiblere Möglichkeit, Bestand zu verwalten, und kann Anpassungen sowie Integrationen mit externen Systemen vereinfachen.&lt;/p&gt;
&lt;h2&gt;6. Storefront-Bootstrap-Upgrade&lt;/h2&gt;
&lt;h3&gt;Überblick der Änderung&lt;/h3&gt;
&lt;p&gt;Die Storefront wurde von Bootstrap 4 auf Bootstrap 5 aktualisiert, und jQuery wurde als Abhängigkeit entfernt.&lt;/p&gt;
&lt;h3&gt;Auswirkung&lt;/h3&gt;
&lt;p&gt;Eigener JavaScript-Code und Templates, die auf jQuery oder Bootstrap-4-Komponenten angewiesen sind, müssen refaktorisiert werden, damit sie zu Bootstrap 5 passen und bei Bedarf natives JavaScript verwenden.&lt;/p&gt;
&lt;h3&gt;Erforderliche Aktion&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Ersetze jQuery-Nutzung durch natives JavaScript oder Bootstrap-5-Utilities.&lt;/li&gt;
&lt;li&gt;Aktualisiere Bootstrap-Klassen und Komponenten, damit sie zur Benennung und Struktur von Bootstrap 5 passen.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Beobachtungen&lt;/h3&gt;
&lt;p&gt;Bootstrap 5 bringt bessere Performance, weniger Abhängigkeiten und modernisierte Komponenten. Die Aktualisierung kann zeitaufwendig sein, bietet aber langfristige Vorteile für Wartbarkeit und Nutzererlebnis.&lt;/p&gt;
&lt;h2&gt;7. Änderungen am Offcanvas-Cart-Data-Attribut (Shopware 6.6)&lt;/h2&gt;
&lt;h3&gt;Überblick der Änderung&lt;/h3&gt;
&lt;p&gt;In Shopware 6.6 gab es eine subtile, aber wichtige Änderung am Data-Attribut, das die Offcanvas-Cart-Funktion auslöst.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Vorheriges Data-Attribut:&lt;/strong&gt; &lt;code class=&quot;language-text&quot;&gt;data-offcanvas-cart&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Neues Data-Attribut:&lt;/strong&gt; &lt;code class=&quot;language-text&quot;&gt;data-off-canvas-cart&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Auswirkung&lt;/h3&gt;
&lt;p&gt;Eigene Templates oder Themes, die das Attribut &lt;code class=&quot;language-text&quot;&gt;data-offcanvas-cart&lt;/code&gt; ohne Bindestriche verwenden, können feststellen, dass der Offcanvas Cart nicht mehr wie erwartet funktioniert, weil der JavaScript-Listener in Shopware 6.6 nach der Variante mit Bindestrichen sucht.&lt;/p&gt;
&lt;h3&gt;Erforderliche Aktion&lt;/h3&gt;
&lt;p&gt;Aktualisiere das Attribut &lt;code class=&quot;language-text&quot;&gt;data-offcanvas-cart&lt;/code&gt; in deinen Templates zu &lt;code class=&quot;language-text&quot;&gt;data-off-canvas-cart&lt;/code&gt;.&lt;/p&gt;
&lt;h3&gt;Beispiel&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;html&quot;&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;&amp;lt;!-- Before --&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;header-cart&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;data-offcanvas-cart&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;true&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;&amp;lt;!-- Cart content --&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;&amp;lt;!-- After --&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;header-cart&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;data-off-canvas-cart&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;true&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;&amp;lt;!-- Cart content --&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3&gt;Beobachtungen&lt;/h3&gt;
&lt;p&gt;Diese Änderung ist in den offiziellen Shopware-6.6-Release-Notes nicht gut dokumentiert, ist aber entscheidend dafür, dass der Offcanvas Cart korrekt funktioniert. Das JavaScript, das die Cart-Funktionalität initialisiert, verlässt sich auf das Attribut &lt;code class=&quot;language-text&quot;&gt;data-off-canvas-cart&lt;/code&gt;, und jede Abweichung kann verhindern, dass der Cart funktioniert.&lt;/p&gt;
&lt;h3&gt;Zusätzliche Hinweise&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Konsistenz ist entscheidend:&lt;/strong&gt; Stelle sicher, dass alle Stellen aktualisiert werden, an denen das Offcanvas-Cart-Attribut verwendet wird.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Gründlich testen:&lt;/strong&gt; Teste nach der Änderung die Cart-Funktionalität, um zu bestätigen, dass sie wie erwartet funktioniert.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Auf ähnliche Änderungen prüfen:&lt;/strong&gt; Andere Data-Attribute oder Event-Listener können ähnliche Updates erhalten haben; prüfe deine eigenen Templates entsprechend.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;8. Änderungen beim CSRF-Schutz&lt;/h2&gt;
&lt;h3&gt;Überblick der Änderung&lt;/h3&gt;
&lt;p&gt;Shopware 6.5 und spätere Versionen haben das explizite CSRF-Token-Handling in Templates entfernt und sind zu einer SameSite-Cookie-Strategie für den CSRF-Schutz übergegangen.&lt;/p&gt;
&lt;h3&gt;Auswirkung&lt;/h3&gt;
&lt;p&gt;Templates und Formulare, die bisher CSRF-Tokens mit der Funktion &lt;code class=&quot;language-text&quot;&gt;sw_csrf&lt;/code&gt; eingebunden haben, erzeugen Fehler, weil diese Funktion nicht mehr existiert.&lt;/p&gt;
&lt;h3&gt;Erforderliche Aktion&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;CSRF-Token-Funktionen entfernen:&lt;/strong&gt; Entferne die Nutzung von &lt;code class=&quot;language-text&quot;&gt;{{ sw_csrf(&apos;route_name&apos;) }}&lt;/code&gt; aus deinen Templates.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Auf SameSite-Cookies vertrauen:&lt;/strong&gt; Verlasse dich auf die eingebaute SameSite-Cookie-Strategie für CSRF-Schutz, die keine expliziten Tokens in Formularen erfordert.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Formularattribute anpassen:&lt;/strong&gt; Stelle sicher, dass Formulare und AJAX-Requests korrekt konfiguriert sind, damit sie mit dem neuen CSRF-Schutzmechanismus funktionieren.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Beispiel&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;twig&quot;&gt;&lt;pre class=&quot;language-twig&quot;&gt;&lt;code class=&quot;language-twig&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;&amp;lt;!-- Before --&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;form&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;action&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token twig language-twig&quot;&gt;&lt;span class=&quot;token delimiter punctuation&quot;&gt;{{&lt;/span&gt; path&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&apos;&lt;/span&gt;frontend.checkout.line-item.add&lt;span class=&quot;token punctuation&quot;&gt;&apos;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token delimiter punctuation&quot;&gt;}}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;method&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;post&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token twig language-twig&quot;&gt;&lt;span class=&quot;token delimiter punctuation&quot;&gt;{{&lt;/span&gt; sw_csrf&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&apos;&lt;/span&gt;frontend.checkout.line-item.add&lt;span class=&quot;token punctuation&quot;&gt;&apos;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token delimiter punctuation&quot;&gt;}}&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;&amp;lt;!-- Form fields --&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;button&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;submit&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Add to Cart&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;button&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;form&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;&amp;lt;!-- After --&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;form&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;action&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token twig language-twig&quot;&gt;&lt;span class=&quot;token delimiter punctuation&quot;&gt;{{&lt;/span&gt; path&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&apos;&lt;/span&gt;frontend.checkout.line-item.add&lt;span class=&quot;token punctuation&quot;&gt;&apos;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token delimiter punctuation&quot;&gt;}}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;method&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;post&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;&amp;lt;!-- Form fields --&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;button&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;submit&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Add to Cart&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;button&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;form&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3&gt;Beobachtungen&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Fehlerbehebung:&lt;/strong&gt; Das Entfernen der Funktion &lt;code class=&quot;language-text&quot;&gt;sw_csrf&lt;/code&gt; behebt den Fehler &quot;Unknown &apos;sw_csrf&apos; function&quot;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Sicherheit bleibt erhalten:&lt;/strong&gt; Die SameSite-Cookie-Strategie schützt weiterhin vor CSRF-Angriffen, ohne zusätzliche Tokens.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Vereinfachte Templates:&lt;/strong&gt; Formulare werden sauberer und etwas einfacher, weil keine CSRF-Tokens mehr nötig sind.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Zusätzliche Hinweise&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Testen ist entscheidend:&lt;/strong&gt; Teste Formularübermittlungen nach diesen Änderungen gründlich, um sicherzustellen, dass sie korrekt funktionieren.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Den neuen Mechanismus verstehen:&lt;/strong&gt; Mach dich damit vertraut, wie die SameSite-Cookie-Strategie funktioniert, um eine sichere Anwendung zu erhalten.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Dokumentation aktualisieren:&lt;/strong&gt; Stelle sicher, dass interne Dokumentation diese Änderung widerspiegelt, damit sie später keine Verwirrung erzeugt.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;9. Umgang mit Problemen beim Offcanvas Cart&lt;/h2&gt;
&lt;h3&gt;Überblick des Szenarios&lt;/h3&gt;
&lt;p&gt;Nach dem Aktualisieren von Templates und dem Entfernen der Funktion &lt;code class=&quot;language-text&quot;&gt;sw_csrf&lt;/code&gt; können Entwickler weiterhin Probleme sehen, bei denen ein Klick auf den Button &quot;Add to Cart&quot; den Offcanvas Cart öffnet, dieser aber leer erscheint.&lt;/p&gt;
&lt;h3&gt;Ursache&lt;/h3&gt;
&lt;p&gt;Der Offcanvas Cart zeigt die hinzugefügten Artikel möglicherweise nicht an, weil Parameter in der Formularübermittlung fehlen oder falsch sind, konkret das fehlende Input-Feld &lt;code class=&quot;language-text&quot;&gt;redirectTo&lt;/code&gt;.&lt;/p&gt;
&lt;h3&gt;Erforderliche Aktion&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Den Parameter &lt;code class=&quot;language-text&quot;&gt;redirectTo&lt;/code&gt; hinzufügen:&lt;/strong&gt; Füge deinen Add-to-Cart-Formularen ein verstecktes Input-Feld mit dem Namen &lt;code class=&quot;language-text&quot;&gt;redirectTo&lt;/code&gt; und dem Wert &lt;code class=&quot;language-text&quot;&gt;frontend.cart.offcanvas&lt;/code&gt; hinzu.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Korrekte Data-Attribute sicherstellen:&lt;/strong&gt; Prüfe, dass alle nötigen Data-Attribute vorhanden und korrekt benannt sind.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Beispiel&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;twig&quot;&gt;&lt;pre class=&quot;language-twig&quot;&gt;&lt;code class=&quot;language-twig&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;form&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;action&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token twig language-twig&quot;&gt;&lt;span class=&quot;token delimiter punctuation&quot;&gt;{{&lt;/span&gt; path&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&apos;&lt;/span&gt;frontend.checkout.line-item.add&lt;span class=&quot;token punctuation&quot;&gt;&apos;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token delimiter punctuation&quot;&gt;}}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;method&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;post&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;input&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;hidden&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;redirectTo&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;frontend.cart.offcanvas&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;input&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;hidden&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;lineItems[&lt;span class=&quot;token twig language-twig&quot;&gt;&lt;span class=&quot;token delimiter punctuation&quot;&gt;{{&lt;/span&gt; product&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;id &lt;span class=&quot;token delimiter punctuation&quot;&gt;}}&lt;/span&gt;&lt;/span&gt;][id]&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token twig language-twig&quot;&gt;&lt;span class=&quot;token delimiter punctuation&quot;&gt;{{&lt;/span&gt; product&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;id &lt;span class=&quot;token delimiter punctuation&quot;&gt;}}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;&amp;lt;!-- Other form fields --&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;button&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;submit&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Add to Cart&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;button&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;form&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3&gt;Beobachtungen&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Funktionalität wiederherstellen:&lt;/strong&gt; Das Hinzufügen des Parameters &lt;code class=&quot;language-text&quot;&gt;redirectTo&lt;/code&gt; teilt Shopware mit, dass der Offcanvas Cart nach dem Hinzufügen eines Artikels geladen werden soll, wodurch der Cart korrekt angezeigt wird.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Auf Details achten:&lt;/strong&gt; Kleine Auslassungen wie fehlende Input-Felder können zu erheblichen Funktionsproblemen führen und zeigen, wie wichtig sorgfältige Codeprüfung ist.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Zusätzliche Hinweise&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Konsistenz bei Data-Attributen:&lt;/strong&gt; Prüfe doppelt, ob Data-Attribute wie &lt;code class=&quot;language-text&quot;&gt;data-product-id&lt;/code&gt; korrekt gesetzt sind.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;JavaScript-Abhängigkeiten prüfen:&lt;/strong&gt; Stelle sicher, dass alle JavaScript-Plugins oder Komponenten rund um den Cart korrekt geladen und initialisiert werden.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Cache leeren:&lt;/strong&gt; Leere nach Änderungen den Shopware-Cache und deinen Browser-Cache, damit veraltete Dateien keine Probleme verursachen.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;10. Erweiterungen am Rule Builder&lt;/h2&gt;
&lt;h3&gt;Überblick der Änderung&lt;/h3&gt;
&lt;p&gt;Die Rule Builder API wurde erweitert, um komplexere bedingte Logik zu unterstützen.&lt;/p&gt;
&lt;h3&gt;Auswirkung&lt;/h3&gt;
&lt;p&gt;Eigene Regeln und Bedingungen müssen möglicherweise angepasst werden, um zu den neuen Interfaces oder Methoden zu passen, die der erweiterte Rule Builder bereitstellt.&lt;/p&gt;
&lt;h3&gt;Erforderliche Aktion&lt;/h3&gt;
&lt;p&gt;Prüfe die Rule-Builder-Dokumentation und aktualisiere eigene Regelimplementierungen, um Kompatibilität sicherzustellen.&lt;/p&gt;
&lt;h3&gt;Beobachtungen&lt;/h3&gt;
&lt;p&gt;Erweiterte Regelmöglichkeiten erlauben präziseres Targeting und mehr Anpassung innerhalb von Shopware. Diese neuen Funktionen zu nutzen kann zu besserer Anpassungsfähigkeit und persönlicheren Erlebnissen für Endnutzer führen.&lt;/p&gt;
&lt;h2&gt;Fazit&lt;/h2&gt;
&lt;p&gt;Shopware 6.5 und 6.6 führen mehrere wichtige Änderungen an Klassen, Namespaces, Data-Attributen und Sicherheitsmechanismen ein, die Entwickler berücksichtigen müssen, um Kompatibilität zu erhalten und neue Funktionen zu nutzen. Die Aktualisierung deiner Codebase verlangt sorgfältige Prüfung und Tests, bietet aber Chancen, Performance, Sicherheit und Funktionalität zu verbessern.&lt;/p&gt;
&lt;h2&gt;Empfehlungen&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Vorausplanen:&lt;/strong&gt; Prüfe vor dem Update die offiziellen Shopware-Release-Notes und Upgrade-Guides für umfassende Informationen.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Gründlich testen:&lt;/strong&gt; Implementiere Änderungen in einer Staging-Umgebung und teste ausführlich, um Probleme zu finden und zu beheben.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Dokumentation nutzen:&lt;/strong&gt; Nutze Shopware-Dokumentation und Community-Foren als Orientierung zu bestimmten Änderungen.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Informiert bleiben:&lt;/strong&gt; Verfolge künftige Updates, um kommende Änderungen früh zu erkennen und dich entsprechend vorzubereiten.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Zusätzliche Beobachtungen&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Auf Details achten:&lt;/strong&gt; Kleine Änderungen, etwa Bindestriche in Data-Attributen oder das Entfernen von Funktionen, können erhebliche Auswirkungen haben. Prüfe Template-Updates immer sorgfältig.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Community-Support:&lt;/strong&gt; Die Shopware-Community ist aktiv und kooperativ. Austausch mit anderen Entwicklern kann Einsichten und Lösungen für häufige Herausforderungen liefern.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Best Practices:&lt;/strong&gt; Aktualisierte Best Practices zu übernehmen, etwa natives JavaScript statt jQuery zu verwenden und auf moderne Sicherheitsstrategien zu setzen, führt zu saubererem und effizienterem Code.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Deprecations beobachten:&lt;/strong&gt; Auf Deprecation-Hinweise zu achten und sich auf künftige Entfernungen vorzubereiten kann Störungen während Upgrades minimieren.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;Wenn Entwickler die Änderungen an Klassen, Namespaces, Data-Attributen und Sicherheit in Shopware 6.5 und 6.6 verstehen und angehen, können sie einen reibungslosen Übergang sicherstellen und weiterhin robuste, zukunftsfähige E-Commerce-Lösungen bauen.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Die Entwicklung von Docker Compose: Was sich geändert hat und warum es wichtig ist]]></title><description><![CDATA[TL;DR: Docker Compose v1 () wurde im April 2025 vollständig entfernt. Das Feld  in deinem YAML ist tot. Der Schlüssel  heißt jetzt einfach…]]></description><link>https://bdteo.com/de/docker-compose-major-changes-since-october-2023/</link><guid isPermaLink="false">https://bdteo.com/de/docker-compose-major-changes-since-october-2023/</guid><pubDate>Wed, 20 Nov 2024 00:00:00 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;TL;DR:&lt;/strong&gt; Docker Compose v1 (&lt;code class=&quot;language-text&quot;&gt;docker-compose&lt;/code&gt;) wurde im April 2025 vollständig entfernt. Das Feld &lt;code class=&quot;language-text&quot;&gt;version&lt;/code&gt; in deinem YAML ist tot. Der Schlüssel &lt;code class=&quot;language-text&quot;&gt;x-develop&lt;/code&gt; heißt jetzt einfach &lt;code class=&quot;language-text&quot;&gt;develop&lt;/code&gt;. Der Watch-Modus ist mit &lt;code class=&quot;language-text&quot;&gt;initial_sync&lt;/code&gt; produktionsreif. Es gibt eine kritische Path-Traversal-CVE (CVE-2025-62725), wenn du &lt;code class=&quot;language-text&quot;&gt;include&lt;/code&gt; mit OCI-Artefakten nutzt -- aktualisiere auf v2.40.2 oder neuer. Und ja, Compose ist von v2 auf v5 gesprungen. Details unten.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Ursprünglich im November 2024 veröffentlicht. Aktualisiert im März 2026 mit Compose v5, CVE-2025-62725, der Entfernung von v1 und neuen Spec-Funktionen.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Wenn du Docker Compose schon eine Weile nutzt, hast du wahrscheinlich bemerkt, dass Dinge unter deinen Füßen brechen oder sich verändern. Die letzten zwei Jahre waren die aggressivste Entwicklung, die Compose je durchlaufen hat -- und nicht alles davon war offensichtlich.&lt;/p&gt;
&lt;p&gt;Ich benutze Compose täglich. Die meisten meiner &lt;a href=&quot;/laravel-sail-vs-laradock-choosing-right-docker-solution/&quot;&gt;Entwicklungs-Setups&lt;/a&gt; laufen darauf. Wenn sich Dinge ändern, merke ich das. Hier ist, was wirklich zählt.&lt;/p&gt;
&lt;h2&gt;Was kaputtging&lt;/h2&gt;
&lt;h3&gt;docker-compose ist tot&lt;/h3&gt;
&lt;p&gt;Nicht deprecated. Nicht im Wartungsmodus. &lt;strong&gt;Tot.&lt;/strong&gt; Das eigenständige &lt;code class=&quot;language-text&quot;&gt;docker-compose&lt;/code&gt;-Binary (die Python-basierte v1) wurde im April 2025 aus GitHub-Actions-Runnern und offiziellen Docker-Images entfernt &lt;small&gt;&lt;a href=&quot;#ref1&quot;&gt;[1]&lt;/a&gt;&lt;/small&gt;. Wenn deine CI/CD-Pipelines noch &lt;code class=&quot;language-text&quot;&gt;docker-compose&lt;/code&gt; mit Bindestrich referenzieren, sind sie kaputt oder kurz davor.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# This no longer works&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;docker-compose&lt;/span&gt; up &lt;span class=&quot;token parameter variable&quot;&gt;-d&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# This is the only way now&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;docker&lt;/span&gt; compose up &lt;span class=&quot;token parameter variable&quot;&gt;-d&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Das Go-basierte &lt;code class=&quot;language-text&quot;&gt;docker compose&lt;/code&gt; (v2, inzwischen v5) ist seit 2022 die eigentliche Implementierung. Die v1-CLI hing aus Kompatibilitätsgründen an der Lebenserhaltung. Diese Lebenserhaltung ist beendet.&lt;/p&gt;
&lt;h3&gt;Das version-Feld ist weg&lt;/h3&gt;
&lt;p&gt;Hör auf, &lt;code class=&quot;language-text&quot;&gt;version: &quot;3.8&quot;&lt;/code&gt; an den Anfang deiner Compose-Dateien zu setzen. Es tut nichts. Es wird seit v2 ignoriert und ist jetzt offiziell deprecated. Moderne Compose-Dateien beginnen mit &lt;code class=&quot;language-text&quot;&gt;services:&lt;/code&gt;.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;yaml&quot;&gt;&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# Stop doing this&lt;/span&gt;
&lt;span class=&quot;token key atrule&quot;&gt;version&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;3.8&quot;&lt;/span&gt;
&lt;span class=&quot;token key atrule&quot;&gt;services&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;web&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;image&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; nginx

&lt;span class=&quot;token comment&quot;&gt;# Just do this&lt;/span&gt;
&lt;span class=&quot;token key atrule&quot;&gt;services&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;web&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;image&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; nginx&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Wenn du &lt;code class=&quot;language-text&quot;&gt;version:&lt;/code&gt; in einem Tutorial siehst, ist dieses Tutorial veraltet.&lt;/p&gt;
&lt;h3&gt;Weitere Deprecations&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code class=&quot;language-text&quot;&gt;links&lt;/code&gt;&lt;/strong&gt; -- nutze Docker-Netzwerke. Links sind seit dem Start von Compose v2 Legacy.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code class=&quot;language-text&quot;&gt;container_name&lt;/code&gt;&lt;/strong&gt; -- lass Docker die Namen verwalten. Fest codierte Namen brechen Skalierung und verursachen Konflikte.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Kurze Volume-Syntax für komplexe Mounts&lt;/strong&gt; -- nutze die Langform mit &lt;code class=&quot;language-text&quot;&gt;type&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;source&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;target&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Was neu und tatsächlich nützlich ist&lt;/h2&gt;
&lt;h3&gt;Watch-Modus (jetzt produktionsreif)&lt;/h3&gt;
&lt;p&gt;Das ist die größte Verbesserung der Lebensqualität seit Jahren. Der Abschnitt &lt;code class=&quot;language-text&quot;&gt;develop&lt;/code&gt; (früher &lt;code class=&quot;language-text&quot;&gt;x-develop&lt;/code&gt; -- lass das Präfix &lt;code class=&quot;language-text&quot;&gt;x-&lt;/code&gt; weg, es ist nicht mehr experimentell) ermöglicht dir, Regeln zur Dateiüberwachung zu definieren, die automatisch synchronisieren oder neu bauen, wenn sich Dateien ändern:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;yaml&quot;&gt;&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;services&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;web&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;build&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; .
    &lt;span class=&quot;token key atrule&quot;&gt;develop&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;watch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; ./src
          &lt;span class=&quot;token key atrule&quot;&gt;action&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; sync
          &lt;span class=&quot;token key atrule&quot;&gt;target&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; /app/src
        &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; ./package.json
          &lt;span class=&quot;token key atrule&quot;&gt;action&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; rebuild&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Drei Aktionen sind verfügbar (seit v2.32.0):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code class=&quot;language-text&quot;&gt;sync&lt;/code&gt;&lt;/strong&gt; -- kopiert geänderte Dateien in den Container, ohne neu zu bauen&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code class=&quot;language-text&quot;&gt;restart&lt;/code&gt;&lt;/strong&gt; -- startet den Service neu, wenn sich Dateien ändern&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code class=&quot;language-text&quot;&gt;rebuild&lt;/code&gt;&lt;/strong&gt; -- löst einen vollständigen Rebuild aus&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Seit September 2025 gibt es außerdem &lt;strong&gt;&lt;code class=&quot;language-text&quot;&gt;initial_sync&lt;/code&gt;&lt;/strong&gt; -- es synchronisiert alle Dateien sofort, wenn du &lt;code class=&quot;language-text&quot;&gt;docker compose watch&lt;/code&gt; startest, sodass du nicht auf die erste Änderung warten musst, um die Synchronisierung auszulösen. Das war lange ein Schmerzpunkt.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;docker&lt;/span&gt; compose &lt;span class=&quot;token function&quot;&gt;watch&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Keine manuellen Rebuilds mehr während der Entwicklung. Das hat meinen Workflow wirklich verändert.&lt;/p&gt;
&lt;h3&gt;Include mit OCI-Artefakten&lt;/h3&gt;
&lt;p&gt;Die Direktive &lt;code class=&quot;language-text&quot;&gt;include&lt;/code&gt; kann jetzt Compose-Fragmente aus OCI-Registries ziehen:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;yaml&quot;&gt;&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;include&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; oci&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;//docker.io/username/my&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;compose&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;fragment&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;latest&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Es gibt außerdem experimentelle Unterstützung für Git-Repositorys. Das ist nützlich, um gemeinsame Service-Definitionen über Projekte hinweg zu teilen -- Datenbankkonfigurationen, Monitoring-Stacks usw.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Aber lies zuerst den Sicherheitsabschnitt unten.&lt;/strong&gt; Es gibt eine CVE.&lt;/p&gt;
&lt;h3&gt;GPU-Unterstützung&lt;/h3&gt;
&lt;p&gt;GPU-Passthrough ist sauberer geworden. Es gibt jetzt neben dem ausführlichen Ansatz über &lt;code class=&quot;language-text&quot;&gt;deploy.resources.reservations.devices&lt;/code&gt; eine kürzere &lt;code class=&quot;language-text&quot;&gt;gpus:&lt;/code&gt;-Syntax (v2.30.0+). AMD-GPU-Unterstützung wurde 2025 offiziell integriert -- nicht mehr nur NVIDIA.&lt;/p&gt;
&lt;h3&gt;Models-Element&lt;/h3&gt;
&lt;p&gt;Die Compose-Spec enthält jetzt ein &lt;code class=&quot;language-text&quot;&gt;models&lt;/code&gt;-Element, um AI/ML-Modelle als OCI-Artefakte zu definieren. Du kannst LLMs und Inference-Runtimes direkt in deinem Compose-Setup paketieren. Nischig, aber interessant, wenn du lokal mit AI arbeitest.&lt;/p&gt;
&lt;h3&gt;Bessere Dependencies&lt;/h3&gt;
&lt;p&gt;Die &lt;code class=&quot;language-text&quot;&gt;depends_on&lt;/code&gt;-Bedingungen sind flexibler geworden:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;yaml&quot;&gt;&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;services&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;web&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;depends_on&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;db&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;token key atrule&quot;&gt;condition&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; service_healthy
        &lt;span class=&quot;token key atrule&quot;&gt;restart&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean important&quot;&gt;true&lt;/span&gt;      &lt;span class=&quot;token comment&quot;&gt;# restart web if db restarts&lt;/span&gt;
        &lt;span class=&quot;token key atrule&quot;&gt;required&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean important&quot;&gt;false&lt;/span&gt;     &lt;span class=&quot;token comment&quot;&gt;# web can start even if db isn&apos;t ready&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Die Optionen &lt;code class=&quot;language-text&quot;&gt;restart: true&lt;/code&gt; und &lt;code class=&quot;language-text&quot;&gt;required: false&lt;/code&gt; sind wirklich nützlich für robuste lokale Entwicklungs-Setups.&lt;/p&gt;
&lt;h2&gt;Was du wissen solltest&lt;/h2&gt;
&lt;h3&gt;CVE-2025-62725: Include Path Traversal&lt;/h3&gt;
&lt;p&gt;Wenn du die Direktive &lt;code class=&quot;language-text&quot;&gt;include&lt;/code&gt; mit OCI-Artefakten nutzt, &lt;strong&gt;aktualisiere sofort auf v2.40.2 oder neuer&lt;/strong&gt; &lt;small&gt;&lt;a href=&quot;#ref2&quot;&gt;[2]&lt;/a&gt;&lt;/small&gt;. Eine Path-Traversal-Schwachstelle erlaubt es einem Angreifer, bei der Artefaktauflösung aus dem Cache-Verzeichnis auszubrechen. Sogar ein harmlos wirkendes &lt;code class=&quot;language-text&quot;&gt;docker compose ps&lt;/code&gt; kann sie auslösen, wenn deine Compose-Datei eine bösartige OCI-Referenz einbindet.&lt;/p&gt;
&lt;p&gt;Docker hat das mit einer &lt;code class=&quot;language-text&quot;&gt;validatePathInBase()&lt;/code&gt;-Prüfung gepatcht, aber du musst auf der gefixten Version sein.&lt;/p&gt;
&lt;h3&gt;Compose v5&lt;/h3&gt;
&lt;p&gt;Docker ist von v2 auf v5 gesprungen (3 und 4 wurden übersprungen, um Verwirrung mit den alten Dateiformat-Versionen zu vermeiden) &lt;small&gt;&lt;a href=&quot;#ref3&quot;&gt;[3]&lt;/a&gt;&lt;/small&gt;. Funktional ist v5 dasselbe wie späte v2-Releases, aber es enthält ein offizielles &lt;strong&gt;Go SDK&lt;/strong&gt; für programmatischen Zugriff -- du kannst Compose-Funktionalität also direkt in Go-Anwendungen einbetten, ohne zur CLI zu shellen.&lt;/p&gt;
&lt;p&gt;Prüfe deine Version:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;docker&lt;/span&gt; compose version
&lt;span class=&quot;token comment&quot;&gt;# Docker Compose version v5.1.0&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3&gt;Bake ist das Standard-Build-Tool&lt;/h3&gt;
&lt;p&gt;Docker Bake (über BuildKit) ist jetzt der Standard für &lt;code class=&quot;language-text&quot;&gt;docker compose build&lt;/code&gt;. Es beherrscht Multi-Target-Builds, Cross-Platform-Kompilierung und fortgeschrittene Caching-Strategien besser als der Legacy-Builder. Wenn du dir &lt;code class=&quot;language-text&quot;&gt;docker-bake.hcl&lt;/code&gt;-Dateien noch nicht angesehen hast, lohnt es sich, sie zu verstehen -- besonders bei komplexen Multi-Service-Builds.&lt;/p&gt;
&lt;h3&gt;Healthcheck-Verbesserungen&lt;/h3&gt;
&lt;p&gt;Mit dem Feld &lt;code class=&quot;language-text&quot;&gt;start_interval&lt;/code&gt; kannst du während der Startup-Grace-Period ein schnelleres Prüfintervall setzen:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;yaml&quot;&gt;&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;services&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;db&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;healthcheck&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;test&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;CMD&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;pg_isready&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;start_period&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 30s
      &lt;span class=&quot;token key atrule&quot;&gt;start_interval&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 2s    &lt;span class=&quot;token comment&quot;&gt;# check every 2s during startup&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;interval&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 30s         &lt;span class=&quot;token comment&quot;&gt;# then every 30s after&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;retries&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Das bedeutet, dass deine abhängigen Services schneller starten, ohne die Healthcheck-Intervalle in Produktion zu kompromittieren.&lt;/p&gt;
&lt;h2&gt;Migrations-Checkliste&lt;/h2&gt;
&lt;p&gt;Wenn du dein Compose-Setup länger nicht aktualisiert hast:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Entferne &lt;code class=&quot;language-text&quot;&gt;version:&lt;/code&gt;&lt;/strong&gt; aus allen Compose-Dateien.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Ersetze &lt;code class=&quot;language-text&quot;&gt;docker-compose&lt;/code&gt;&lt;/strong&gt; durch &lt;code class=&quot;language-text&quot;&gt;docker compose&lt;/code&gt; in allen Skripten und CI-Konfigurationen.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Benenne &lt;code class=&quot;language-text&quot;&gt;x-develop&lt;/code&gt;&lt;/strong&gt; in Watch-Konfigurationen in &lt;code class=&quot;language-text&quot;&gt;develop&lt;/code&gt; um.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Aktualisiere auf v2.40.2+&lt;/strong&gt;, wenn du &lt;code class=&quot;language-text&quot;&gt;include&lt;/code&gt; nutzt (CVE-2025-62725).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Ersetze &lt;code class=&quot;language-text&quot;&gt;links&lt;/code&gt;&lt;/strong&gt; durch Docker-Netzwerke, falls du sie irgendwie noch nutzt.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Teste deine CI&lt;/strong&gt; -- GitHub Actions hat die Runner im Februar 2026 auf Compose v2.40.3 aktualisiert &lt;small&gt;&lt;a href=&quot;#ref4&quot;&gt;[4]&lt;/a&gt;&lt;/small&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h3&gt;Referenzen&lt;/h3&gt;
&lt;p&gt;&lt;a id=&quot;ref1&quot;&gt;&lt;/a&gt;1. &lt;a href=&quot;https://github.com/actions/runner-images/issues/9557&quot;&gt;Docker Compose v1 removed from runner images (April 2025)&lt;/a&gt; -- &lt;em&gt;GitHub-Actions-Ankündigung zur Entfernung von v1.&lt;/em&gt;&lt;br&gt;
&lt;a id=&quot;ref2&quot;&gt;&lt;/a&gt;2. &lt;a href=&quot;https://www.imperva.com/blog/cve-2025-62725-from-docker-compose-ps-to-system-compromise/&quot;&gt;CVE-2025-62725: From &quot;docker compose ps&quot; to System Compromise&lt;/a&gt; -- &lt;em&gt;Impervas detaillierter Bericht zur Include-Path-Traversal-Schwachstelle.&lt;/em&gt;&lt;br&gt;
&lt;a id=&quot;ref3&quot;&gt;&lt;/a&gt;3. &lt;a href=&quot;https://github.com/docker/compose/releases&quot;&gt;Docker Compose Releases&lt;/a&gt; -- &lt;em&gt;Offizielle Release-Historie inklusive v5.&lt;/em&gt;&lt;br&gt;
&lt;a id=&quot;ref4&quot;&gt;&lt;/a&gt;4. &lt;a href=&quot;https://github.blog/changelog/2026-01-30-docker-and-docker-compose-version-upgrades-on-hosted-runners/&quot;&gt;Docker and Docker Compose version upgrades on hosted runners&lt;/a&gt; -- &lt;em&gt;GitHubs Runner-Update vom Februar 2026.&lt;/em&gt;&lt;br&gt;
&lt;a id=&quot;ref5&quot;&gt;&lt;/a&gt;5. &lt;a href=&quot;https://docs.docker.com/compose/compose-file/&quot;&gt;Compose Specification&lt;/a&gt; -- &lt;em&gt;Offizielle Compose-Dateireferenz.&lt;/em&gt;&lt;br&gt;
&lt;a id=&quot;ref6&quot;&gt;&lt;/a&gt;6. &lt;a href=&quot;https://docs.docker.com/compose/how-tos/file-watch/&quot;&gt;Use Compose Watch&lt;/a&gt; -- &lt;em&gt;Dockers Dokumentation zum Watch-Modus.&lt;/em&gt;&lt;br&gt;
&lt;a id=&quot;ref7&quot;&gt;&lt;/a&gt;7. &lt;a href=&quot;https://docs.docker.com/compose/how-tos/gpu-support/&quot;&gt;Enable GPU Support in Docker Compose&lt;/a&gt; -- &lt;em&gt;GPU-Passthrough-Dokumentation inklusive AMD-Unterstützung.&lt;/em&gt;&lt;br&gt;
&lt;a id=&quot;ref8&quot;&gt;&lt;/a&gt;8. &lt;a href=&quot;https://docs.docker.com/compose/how-tos/multiple-compose-files/include/&quot;&gt;Docker Compose Include&lt;/a&gt; -- &lt;em&gt;Include-Direktive mit OCI- und Git-Unterstützung.&lt;/em&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h3&gt;Verwandte Beiträge&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;/laravel-sail-vs-laradock-choosing-right-docker-solution/&quot;&gt;Laravel Sail vs Laradock: Die richtige Docker-Lösung wählen&lt;/a&gt; -- ein Vergleich Docker-basierter PHP-Entwicklungsumgebungen.&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[Nutze die Kraft von 'git grep' für effiziente Codesuche]]></title><description><![CDATA[In einem weiten Königreich voller zahlloser Schriftrollen und Manuskripte lebte ein Gelehrter namens Alaric. Seine Bibliothek war gewaltig…]]></description><link>https://bdteo.com/de/unlocking-the-power-of-git-grep/</link><guid isPermaLink="false">https://bdteo.com/de/unlocking-the-power-of-git-grep/</guid><pubDate>Wed, 13 Nov 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;In einem weiten Königreich voller zahlloser Schriftrollen und Manuskripte lebte ein Gelehrter namens Alaric. Seine Bibliothek war gewaltig - ein Labyrinth des Wissens, in dem alte Texte neben zeitgenössischen Schriften standen und Geheimnisse zwischen den Zeilen verborgen lagen. Alaric suchte oft nach einer einzigen schwer greifbaren Formulierung in diesem Meer aus Information, eine Aufgabe, die mit jedem Tag einschüchternder wurde.&lt;/p&gt;
&lt;p&gt;Eines Morgens, als die Sonne goldene Strahlen auf die staubigen Folianten warf, machte Alaric sich daran, ein bestimmtes Konzept in seinen Archiven zu finden, das nur als &quot;The Whispering Sigil&quot; bekannt war. Er arbeitete sich durch Band um Band und nutzte seine üblichen Methoden, um die Seiten zu durchsieben - Methoden, die ihm inzwischen träge und ungenau vorkamen. Je tiefer er suchte, desto stärker verhedderte er sich in irrelevanten Passagen, Duplikaten und irreführenden Verweisen. Die Frustration wuchs, als aus Stunden Tage wurden und kaum Fortschritt zu sehen war.&lt;/p&gt;
&lt;p&gt;Dann besuchte ein alter Weiser Alaric und bemerkte seine Not. Mit wissendem Lächeln sagte der Weise: &quot;Vielleicht suchst du auf die mühsame Art. Es gibt einen verborgenen Pfad, den nur jene kennen, die ihr Wissen klug ordnen.&quot; Neugierig hörte Alaric zu, während der Weise ihm eine Methode erklärte, die seine Suche fokussierte, das Durcheinander durchtrennte und ihn direkt zu den Texten führte, die er suchte.&lt;/p&gt;
&lt;p&gt;Mit diesem neuen Ansatz bewaffnet, versuchte Alaric es erneut. Diesmal verblasste der irrelevante Ballast. Der Weg zu &quot;The Whispering Sigil&quot; wurde klar, und er fand mit erstaunlicher Geschwindigkeit, wonach er gesucht hatte. Es war, als hätte er ein geheimes Tor in seinem Labyrinth aufgeschlossen, das ihm schnellen Zugriff auf genau das Wissen gab, das er brauchte.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Puff!&lt;/strong&gt; Das Geheimnis war gelüftet: die Kraft von &lt;code class=&quot;language-text&quot;&gt;git grep&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;Was &lt;code class=&quot;language-text&quot;&gt;git grep&lt;/code&gt; eigentlich ist&lt;/h2&gt;
&lt;p&gt;Plain &lt;code class=&quot;language-text&quot;&gt;grep -r&lt;/code&gt; läuft durch das Dateisystem. Es liest pflichtbewusst alles, was ihm in den Weg kommt: Quellcode, Logdateien, Build-Ausgaben, diese verirrte 4-MB-Dumpdatei, die dein Kollege vergessen hat zu löschen, den gesamten &lt;code class=&quot;language-text&quot;&gt;node_modules&lt;/code&gt;-Baum. &lt;code class=&quot;language-text&quot;&gt;git grep&lt;/code&gt; macht etwas Schmaleres: Es durchsucht die Dateien, die Git bereits kennt. Aus genau dieser einen Designentscheidung kommt der größte Teil seines Werts.&lt;/p&gt;
&lt;h3&gt;Worin &lt;code class=&quot;language-text&quot;&gt;git grep&lt;/code&gt; gut ist&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Es durchsucht getrackte Dateien, nicht das Dateisystem.&lt;/strong&gt; Git hält eine Liste jeder Datei, die du je gestaged oder committet hast - den Index. &lt;code class=&quot;language-text&quot;&gt;git grep&lt;/code&gt; liest aus dieser Liste. Ungetrackter Müll ist dort schlicht nicht vorhanden. Kein &lt;code class=&quot;language-text&quot;&gt;node_modules/&lt;/code&gt;, kein &lt;code class=&quot;language-text&quot;&gt;dist/&lt;/code&gt;, keine Coverage-Reports, keine zufällige Logdatei - weil Git nie von ihnen erfahren hat.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Es ist auf großen Repos schneller als &lt;code class=&quot;language-text&quot;&gt;grep -r&lt;/code&gt;.&lt;/strong&gt; Die Dateiliste hat es bereits, also überspringt es den Lauf durchs Dateisystem. Es läuft mit mehreren Threads parallel. Der Gewinn ist real, aber keine Magie. &lt;code class=&quot;language-text&quot;&gt;git grep&lt;/code&gt; iteriert über dieselben Blobs wie &lt;code class=&quot;language-text&quot;&gt;grep&lt;/code&gt;, nur mit weniger Zeremonie. Es gibt keinen Content-Suchindex - der &quot;Git index&quot; ist eine Liste aus Dateipfaden und Blob-Hashes, kein invertierter Index im Lucene-Stil.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Es kann jede Ref ohne Checkout durchsuchen.&lt;/strong&gt; Das ist das Killer-Feature. Ein Tag, ein Branch, ein Commit, ein Tree-Objekt - zeig mit &lt;code class=&quot;language-text&quot;&gt;git grep&lt;/code&gt; direkt darauf. Kein &lt;code class=&quot;language-text&quot;&gt;git checkout&lt;/code&gt;, kein Stash-Tanz, kein Umweg weg von dem, was du gerade getan hast.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Praktische Beispiele&lt;/h3&gt;
&lt;h4&gt;Einfache Suche&lt;/h4&gt;
&lt;p&gt;Um in deinem Repository nach einem bestimmten Begriff wie &lt;code class=&quot;language-text&quot;&gt;&quot;initializeSettings&quot;&lt;/code&gt; zu suchen:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;git&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;grep&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;initializeSettings&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Das durchsucht alle getrackten Dateien im aktuellen Branch nach der exakten Übereinstimmung.&lt;/p&gt;
&lt;h4&gt;Suche ohne Beachtung der Groß-/Kleinschreibung&lt;/h4&gt;
&lt;p&gt;Für eine Suche ohne Beachtung der Groß-/Kleinschreibung, hilfreich, wenn du dir bei der Kapitalisierung unsicher bist:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;git&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;grep&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-i&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;initializesettings&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Das findet Treffer unabhängig von Unterschieden in der Groß-/Kleinschreibung.&lt;/p&gt;
&lt;h4&gt;Suche in einem bestimmten Branch&lt;/h4&gt;
&lt;p&gt;Um in einem anderen Branch zu suchen, ohne zu ihm zu wechseln, zum Beispiel in &lt;code class=&quot;language-text&quot;&gt;feature/login&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;git&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;grep&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;validateUser&quot;&lt;/span&gt; feature/login&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Das ist der Zug, der schwer zu schlagen ist. Kein Checkout, kein Stash, einfach die Antwort.&lt;/p&gt;
&lt;h4&gt;Suche über alle Branches hinweg&lt;/h4&gt;
&lt;p&gt;Um einen Begriff in jedem Branch zu suchen, einschließlich Remotes:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;git&lt;/span&gt; branch &lt;span class=&quot;token parameter variable&quot;&gt;-a&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;xargs&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-n&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;git&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;grep&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;configureDatabase&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Um in jedem Commit zu suchen, von dem Git je gehört hat, nicht nur an der Spitze eines Branches:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;git&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;grep&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;configureDatabase&quot;&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;git&lt;/span&gt; rev-list &lt;span class=&quot;token parameter variable&quot;&gt;--all&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Das findet Treffer in jedem Blob irgendwo in deiner History. In einem lebhaften Repo kann es einen Moment dauern - es läuft buchstäblich durch jeden Commit.&lt;/p&gt;
&lt;h4&gt;Suche in der Commit-History&lt;/h4&gt;
&lt;p&gt;Um herauszufinden, wann ein bestimmter String hinzugefügt oder entfernt wurde, nutze:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;git&lt;/span&gt; log &lt;span class=&quot;token parameter variable&quot;&gt;-S&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;optimizePerformance&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Das zeigt Commits, die den Begriff &lt;code class=&quot;language-text&quot;&gt;&quot;optimizePerformance&quot;&lt;/code&gt; eingeführt oder entfernt haben.&lt;/p&gt;
&lt;p&gt;Um die tatsächlichen Diffs zu sehen, in denen der Begriff hinzugefügt oder entfernt wurde:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;git&lt;/span&gt; log &lt;span class=&quot;token parameter variable&quot;&gt;-G&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;optimizePerformance&quot;&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-p&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h4&gt;Reguläre Ausdrücke verwenden&lt;/h4&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;git grep&lt;/code&gt; unterstützt reguläre Ausdrücke für fortgeschrittenere Suchen:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;git&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;grep&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-E&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;def\s+\w+\(&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Das matcht Python-Funktionsdefinitionen: &lt;code class=&quot;language-text&quot;&gt;def&lt;/code&gt;, Whitespace, ein Funktionsname, dann eine literale öffnende Klammer. (In Extended Regex ist &lt;code class=&quot;language-text&quot;&gt;\(&lt;/code&gt; eine literale Klammer, während &lt;code class=&quot;language-text&quot;&gt;(&lt;/code&gt; eine Gruppe bedeuten würde; deshalb steht der Backslash dort.)&lt;/p&gt;
&lt;h3&gt;Was &lt;code class=&quot;language-text&quot;&gt;git grep&lt;/code&gt; liest und was nicht&lt;/h3&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;git grep&lt;/code&gt; läuft durch den Index. Das war&apos;s. Es parst &lt;code class=&quot;language-text&quot;&gt;.gitignore&lt;/code&gt; nicht. Viele Leute, einschließlich einer früheren Version dieses Posts, behaupten, es tue das - und die Behauptung ist fast wahr, ungefähr so, wie &quot;die Erde ist flach&quot; fast wahr ist, wenn man immer nur auf einen Parkplatz schaut.&lt;/p&gt;
&lt;p&gt;Die beiden Dinge decken sich nur, weil gitignored Dateien normalerweise auch ungetrackt sind. In dem Moment, in dem eine Datei &lt;em&gt;sowohl&lt;/em&gt; gitignored &lt;em&gt;als auch&lt;/em&gt; getrackt ist - jemand hat &lt;code class=&quot;language-text&quot;&gt;git add -f&lt;/code&gt; ausgeführt, oder die Datei wurde committet, bevor die Regel existierte -, durchsucht &lt;code class=&quot;language-text&quot;&gt;git grep&lt;/code&gt; sie fröhlich. &lt;code class=&quot;language-text&quot;&gt;rg&lt;/code&gt; tut das nicht.&lt;/p&gt;
&lt;p&gt;Du kannst das in zwanzig Sekunden beweisen:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;mkdir&lt;/span&gt; demo &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;token builtin class-name&quot;&gt;cd&lt;/span&gt; demo
&lt;span class=&quot;token function&quot;&gt;git&lt;/span&gt; init &lt;span class=&quot;token parameter variable&quot;&gt;-q&lt;/span&gt;
&lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;*.log&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; .gitignore
&lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;the secret phrase&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; tracked.log
&lt;span class=&quot;token function&quot;&gt;git&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;add&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-f&lt;/span&gt; tracked.log .gitignore
&lt;span class=&quot;token function&quot;&gt;git&lt;/span&gt; commit &lt;span class=&quot;token parameter variable&quot;&gt;-qm&lt;/span&gt; init

&lt;span class=&quot;token function&quot;&gt;git&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;grep&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;secret phrase&quot;&lt;/span&gt;   &lt;span class=&quot;token comment&quot;&gt;# finds it - the file is tracked, ignore rule notwithstanding&lt;/span&gt;
rg &lt;span class=&quot;token string&quot;&gt;&quot;secret phrase&quot;&lt;/span&gt;         &lt;span class=&quot;token comment&quot;&gt;# finds nothing - rg actually reads .gitignore&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Die genaue Aussage lautet also: &lt;code class=&quot;language-text&quot;&gt;git grep&lt;/code&gt; durchsucht getrackte Dateien. Dadurch überspringt es zufällig &lt;em&gt;das meiste&lt;/em&gt; von dem, was &lt;code class=&quot;language-text&quot;&gt;.gitignore&lt;/code&gt; ebenfalls überspringen würde, aber der Mechanismus ist ein anderer, und der Randfall ist wichtig - besonders dann, wenn du nach einem String suchst, der am Ende in einer generierten Datei lebt, die vor Jahren jemand mit Gewalt hinzugefügt hat.&lt;/p&gt;
&lt;p&gt;Der &lt;code class=&quot;language-text&quot;&gt;.gitignore&lt;/code&gt;-Mechanismus kommt nur über zwei Opt-in-Modi in &lt;code class=&quot;language-text&quot;&gt;git grep&lt;/code&gt; hinein:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;--untracked&lt;/code&gt; - auch ungetrackte Dateien durchsuchen. &lt;em&gt;In diesem Modus&lt;/em&gt; respektiert &lt;code class=&quot;language-text&quot;&gt;git grep&lt;/code&gt; standardmäßig &lt;code class=&quot;language-text&quot;&gt;.gitignore&lt;/code&gt; und überspringt ignorierte Dateien (mit &lt;code class=&quot;language-text&quot;&gt;--no-exclude-standard&lt;/code&gt; kannst du das überschreiben und sie ebenfalls durchsuchen).&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;--no-index&lt;/code&gt; - das aktuelle Verzeichnis durchsuchen und Git vollständig ignorieren. Nützlich innerhalb eines Repos, wenn du plain-grep-Semantik willst. In diesem Modus konsultiert &lt;code class=&quot;language-text&quot;&gt;git grep&lt;/code&gt; &lt;code class=&quot;language-text&quot;&gt;.gitignore&lt;/code&gt; standardmäßig &lt;em&gt;nicht&lt;/em&gt; - nutze &lt;code class=&quot;language-text&quot;&gt;--exclude-standard&lt;/code&gt;, wenn du das möchtest.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Standard-&lt;code class=&quot;language-text&quot;&gt;git grep&lt;/code&gt;, ohne Flags, öffnet deine &lt;code class=&quot;language-text&quot;&gt;.gitignore&lt;/code&gt;-Datei nie.&lt;/p&gt;
&lt;h2&gt;Wann du stattdessen zu &lt;code class=&quot;language-text&quot;&gt;rg&lt;/code&gt; greifen solltest&lt;/h2&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;git grep&lt;/code&gt; und &lt;code class=&quot;language-text&quot;&gt;rg&lt;/code&gt; (ripgrep) sind eigentlich keine Konkurrenten. Sie laufen durch unterschiedliche Dinge, und eine ernsthafte Werkzeugkiste hat beide.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;git grep&lt;/code&gt; läuft durch &lt;strong&gt;den Index&lt;/strong&gt;: getrackte Dateien, plus jede Ref oder jedes Tree-Objekt, auf das du zeigst.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;rg&lt;/code&gt; läuft durch &lt;strong&gt;das Dateisystem&lt;/strong&gt;: jede Datei unter dem aktuellen Verzeichnis, abzüglich dessen, was deine &lt;code class=&quot;language-text&quot;&gt;.gitignore&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;.ignore&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;.rgignore&lt;/code&gt; und globalen Excludes ihm zu überspringen sagen.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Jedes der beiden kann etwas, was das andere nicht kann.&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;git grep&lt;/code&gt; gewinnt, wenn du über die History hinweg suchen willst, ohne einen Checkout zu machen:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;git&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;grep&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;deprecated_api&quot;&lt;/span&gt; v2.3.0          &lt;span class=&quot;token comment&quot;&gt;# search a tag&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;git&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;grep&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;deprecated_api&quot;&lt;/span&gt; HEAD~50         &lt;span class=&quot;token comment&quot;&gt;# 50 commits ago&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;git&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;grep&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;deprecated_api&quot;&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;git&lt;/span&gt; rev-list &lt;span class=&quot;token parameter variable&quot;&gt;--all&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;)&lt;/span&gt;&lt;/span&gt;   &lt;span class=&quot;token comment&quot;&gt;# every commit, ever&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;rg&lt;/code&gt; gewinnt, wenn du tatsächlich Dateisystem-Semantik mit korrekter gitignore-Behandlung willst - einschließlich eines frisch geklonten Unterordners, den du noch nicht mit &lt;code class=&quot;language-text&quot;&gt;git add&lt;/code&gt; aufgenommen hast, generierter Dateien, von denen Git nie gehört hat, oder eines Verzeichnisses, das gar kein Git-Repo ist:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;rg &lt;span class=&quot;token string&quot;&gt;&quot;deprecated_api&quot;&lt;/span&gt;                &lt;span class=&quot;token comment&quot;&gt;# respects .gitignore by default&lt;/span&gt;
rg --no-ignore &lt;span class=&quot;token string&quot;&gt;&quot;deprecated_api&quot;&lt;/span&gt;    &lt;span class=&quot;token comment&quot;&gt;# opt back into ignored files&lt;/span&gt;
rg &lt;span class=&quot;token parameter variable&quot;&gt;--hidden&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;deprecated_api&quot;&lt;/span&gt;       &lt;span class=&quot;token comment&quot;&gt;# include dotfiles&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;rg&lt;/code&gt; ist auch die Engine hinter der Projektsuche von VS Code, weshalb sich &quot;Find in Files&quot; exakt so anfühlt wie &lt;code class=&quot;language-text&quot;&gt;rg&lt;/code&gt; im Terminal. Es hat solide Unicode-Unterstützung, und auf den meisten modernen Corpora ist es mindestens so schnell wie &lt;code class=&quot;language-text&quot;&gt;git grep&lt;/code&gt;, oft schneller - der &lt;a href=&quot;https://github.com/BurntSushi/ripgrep/blob/master/README.md&quot;&gt;Linux-Kernel-Benchmark im ripgrep README&lt;/a&gt; zeigt ripgrep bei derselben Query ungefähr 3x schneller als &lt;code class=&quot;language-text&quot;&gt;git grep -P&lt;/code&gt;. (Tipp: Wenn du das Verhalten &quot;case-sensitive nur dann, wenn dein Pattern Großbuchstaben enthält&quot; willst, übergib &lt;code class=&quot;language-text&quot;&gt;-S&lt;/code&gt; für smart-case - es ist Opt-in, nicht der Default.)&lt;/p&gt;
&lt;p&gt;Falls du &lt;code class=&quot;language-text&quot;&gt;rg&lt;/code&gt; noch nicht installiert hast, behebe das:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;brew &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; ripgrep   &lt;span class=&quot;token comment&quot;&gt;# macOS&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;apt&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; ripgrep    &lt;span class=&quot;token comment&quot;&gt;# Debian/Ubuntu&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;cargo&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; ripgrep  &lt;span class=&quot;token comment&quot;&gt;# anywhere with Rust&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Leg &lt;code class=&quot;language-text&quot;&gt;rg&lt;/code&gt; neben &lt;code class=&quot;language-text&quot;&gt;git grep&lt;/code&gt; in deine Werkzeugkiste. Sie decken unterschiedliche Jobs ab.&lt;/p&gt;
&lt;h3&gt;Vorteile von &lt;code class=&quot;language-text&quot;&gt;git grep&lt;/code&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Relevanz.&lt;/strong&gt; Es durchsucht nur, was du trackst. Build-Ausgaben, Caches und &lt;code class=&quot;language-text&quot;&gt;node_modules&lt;/code&gt; stehen dir nicht im Weg - weil Git sie nie gesehen hat.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Geschwindigkeit auf großen Repos.&lt;/strong&gt; Multithreaded, kein Dateisystemlauf.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;History-Reichweite.&lt;/strong&gt; Jeder Branch, Tag oder Commit, ohne deinen Working Tree zu verlassen. Das ist der Teil, den &lt;code class=&quot;language-text&quot;&gt;rg&lt;/code&gt; nicht kann.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Weniger Binärrauschen.&lt;/strong&gt; Wie &lt;code class=&quot;language-text&quot;&gt;grep&lt;/code&gt; markiert &lt;code class=&quot;language-text&quot;&gt;git grep&lt;/code&gt; Binärdateien mit &quot;Binary file X matches&quot;, statt Bytes auszuspucken - aber weil es durch getrackte Dateien läuft, begegnet es normalerweise von Anfang an weniger davon. Übergib &lt;code class=&quot;language-text&quot;&gt;-I&lt;/code&gt;, um Binärdateien vollständig zu überspringen.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Zusätzliche Tipps&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Ergebnisse paginieren:&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;git&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;grep&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;searchTerm&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;less&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Treffer pro Datei zählen:&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;git&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;grep&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-c&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;searchTerm&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Zeilennummern anzeigen:&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;git&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;grep&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-n&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;searchTerm&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Jeden Treffer im Editor öffnen:&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;git&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;grep&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-l&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;searchTerm&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;xargs&lt;/span&gt; code&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Tausche &lt;code class=&quot;language-text&quot;&gt;code&lt;/code&gt; gegen &lt;code class=&quot;language-text&quot;&gt;nvim&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;subl&lt;/code&gt; oder was auch immer du benutzt.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Fazit&lt;/h2&gt;
&lt;p&gt;So wie Alaric in seiner labyrinthischen Bibliothek einen verborgenen Pfad fand, zieht &lt;code class=&quot;language-text&quot;&gt;git grep&lt;/code&gt; eine saubere Linie durch eine getrackte Codebase: schnell, branch-bewusst und frei von allem, wovon Git nie erfahren hat. Es ist kein universeller Ersatz für &lt;code class=&quot;language-text&quot;&gt;grep&lt;/code&gt;, und es ist kein Ersatz für &lt;code class=&quot;language-text&quot;&gt;rg&lt;/code&gt;. Es ist das Werkzeug, das den &lt;em&gt;Index&lt;/em&gt; deines Repos kennt, und sobald du danach greifst, wird das Labyrinth deutlich kleiner.&lt;/p&gt;
&lt;p&gt;Nutze &lt;code class=&quot;language-text&quot;&gt;git grep&lt;/code&gt;, wenn die Frage lautet: &quot;wo in dieser Codebase, einschließlich ihrer History&quot;. Nutze &lt;code class=&quot;language-text&quot;&gt;rg&lt;/code&gt;, wenn die Frage lautet: &quot;wo auf der Platte, unter Beachtung meiner Ignore-Regeln&quot;. An den meisten Tagen wirst du beide in Reichweite haben wollen.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;Aktualisiert am 2026-04-27: korrigierte eine frühere Behauptung, dass &lt;code class=&quot;language-text&quot;&gt;git grep&lt;/code&gt; &lt;code class=&quot;language-text&quot;&gt;.gitignore&lt;/code&gt; respektiert (tut es nicht direkt), entschärfte die Erklärung zu &quot;internem Indexing&quot;, korrigierte ein Regex-Beispiel und fügte einen Abschnitt dazu hinzu, wann stattdessen &lt;code class=&quot;language-text&quot;&gt;rg&lt;/code&gt; zu verwenden ist.&lt;/em&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[PHP 8.3.6 + IMAP auf macOS mit phpenv: Installationsanleitung]]></title><description><![CDATA[Wichtig (Update 2024-11): PHP 8.4 hat ext-imap aus dem Core ausgelagert. Die Extension ist zu PECL gewandert und praktisch deprecated -- die…]]></description><link>https://bdteo.com/de/installing-php-8-3-6-with-imap-on-macos-using-phpenv/</link><guid isPermaLink="false">https://bdteo.com/de/installing-php-8-3-6-with-imap-on-macos-using-phpenv/</guid><pubDate>Sun, 01 Sep 2024 00:00:00 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Wichtig (Update 2024-11):&lt;/strong&gt; PHP 8.4 hat &lt;strong&gt;ext-imap aus dem Core ausgelagert&lt;/strong&gt;. Die Extension ist zu PECL gewandert und praktisch deprecated -- die zugrunde liegende C-Bibliothek (libc-client) wurde seit 2018 nicht mehr aktualisiert. Wenn du ein neues Projekt startest oder auf PHP 8.4+ bist, spring zu &lt;a href=&quot;#brauchst-du-ext-imap-wirklich&quot;&gt;Brauchst du ext-imap wirklich?&lt;/a&gt; für moderne Alternativen. Wenn du auf PHP 8.3 oder älter bist und die native Extension brauchst, funktioniert diese Anleitung weiterhin.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Der Quick Fix&lt;/h2&gt;
&lt;p&gt;Wenn du nur die Befehle willst und dir das Warum egal ist -- hier ist alles in der richtigen Reihenfolge. Homebrew und phpenv müssen bereits installiert sein.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# 1. Install all required libraries&lt;/span&gt;
brew &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; tidy-html5 openssl@3 zlib &lt;span class=&quot;token function&quot;&gt;bzip2&lt;/span&gt; libedit readline &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
  gettext libiconv libsodium krb5 imap-uw &lt;span class=&quot;token function&quot;&gt;curl&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# 2. Set build configuration (add to ~/.zshrc or run before install)&lt;/span&gt;
&lt;span class=&quot;token builtin class-name&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token assign-left variable&quot;&gt;PHP_BUILD_CONFIGURE_OPTS&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;--with-openssl=&lt;span class=&quot;token variable&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$(&lt;/span&gt;brew &lt;span class=&quot;token parameter variable&quot;&gt;--prefix&lt;/span&gt; openssl@3&lt;span class=&quot;token variable&quot;&gt;)&lt;/span&gt;&lt;/span&gt; \
  --with-zlib=&lt;span class=&quot;token variable&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$(&lt;/span&gt;brew &lt;span class=&quot;token parameter variable&quot;&gt;--prefix&lt;/span&gt; zlib&lt;span class=&quot;token variable&quot;&gt;)&lt;/span&gt;&lt;/span&gt; \
  --with-bz2=&lt;span class=&quot;token variable&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$(&lt;/span&gt;brew &lt;span class=&quot;token parameter variable&quot;&gt;--prefix&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;bzip2&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;)&lt;/span&gt;&lt;/span&gt; \
  --with-curl \
  --with-libedit \
  --with-readline=&lt;span class=&quot;token variable&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$(&lt;/span&gt;brew &lt;span class=&quot;token parameter variable&quot;&gt;--prefix&lt;/span&gt; readline&lt;span class=&quot;token variable&quot;&gt;)&lt;/span&gt;&lt;/span&gt; \
  --with-gettext=&lt;span class=&quot;token variable&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$(&lt;/span&gt;brew &lt;span class=&quot;token parameter variable&quot;&gt;--prefix&lt;/span&gt; gettext&lt;span class=&quot;token variable&quot;&gt;)&lt;/span&gt;&lt;/span&gt; \
  --with-iconv=&lt;span class=&quot;token variable&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$(&lt;/span&gt;brew &lt;span class=&quot;token parameter variable&quot;&gt;--prefix&lt;/span&gt; libiconv&lt;span class=&quot;token variable&quot;&gt;)&lt;/span&gt;&lt;/span&gt; \
  --with-sodium=&lt;span class=&quot;token variable&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$(&lt;/span&gt;brew &lt;span class=&quot;token parameter variable&quot;&gt;--prefix&lt;/span&gt; libsodium&lt;span class=&quot;token variable&quot;&gt;)&lt;/span&gt;&lt;/span&gt; \
  --with-kerberos=&lt;span class=&quot;token variable&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$(&lt;/span&gt;brew &lt;span class=&quot;token parameter variable&quot;&gt;--prefix&lt;/span&gt; krb5&lt;span class=&quot;token variable&quot;&gt;)&lt;/span&gt;&lt;/span&gt; \
  --with-imap=&lt;span class=&quot;token variable&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$(&lt;/span&gt;brew &lt;span class=&quot;token parameter variable&quot;&gt;--prefix&lt;/span&gt; imap-uw&lt;span class=&quot;token variable&quot;&gt;)&lt;/span&gt;&lt;/span&gt; \
  --with-imap-ssl=&lt;span class=&quot;token variable&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$(&lt;/span&gt;brew &lt;span class=&quot;token parameter variable&quot;&gt;--prefix&lt;/span&gt; openssl@3&lt;span class=&quot;token variable&quot;&gt;)&lt;/span&gt;&lt;/span&gt; \
  --with-tidy=&lt;span class=&quot;token variable&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$(&lt;/span&gt;brew &lt;span class=&quot;token parameter variable&quot;&gt;--prefix&lt;/span&gt; tidy-html5&lt;span class=&quot;token variable&quot;&gt;)&lt;/span&gt;&lt;/span&gt; \
  --enable-mbstring \
  --enable-intl&quot;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# 3. Help the compiler find headers&lt;/span&gt;
&lt;span class=&quot;token builtin class-name&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token assign-left variable&quot;&gt;CPPFLAGS&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;-I&lt;span class=&quot;token variable&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$(&lt;/span&gt;brew &lt;span class=&quot;token parameter variable&quot;&gt;--prefix&lt;/span&gt; openssl@3&lt;span class=&quot;token variable&quot;&gt;)&lt;/span&gt;&lt;/span&gt;/include -I&lt;span class=&quot;token variable&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$(&lt;/span&gt;brew &lt;span class=&quot;token parameter variable&quot;&gt;--prefix&lt;/span&gt; imap-uw&lt;span class=&quot;token variable&quot;&gt;)&lt;/span&gt;&lt;/span&gt;/include &lt;span class=&quot;token variable&quot;&gt;$CPPFLAGS&lt;/span&gt;&quot;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# 4. Build and install&lt;/span&gt;
phpenv &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;8.3&lt;/span&gt;.6
phpenv global &lt;span class=&quot;token number&quot;&gt;8.3&lt;/span&gt;.6

&lt;span class=&quot;token comment&quot;&gt;# 5. Verify&lt;/span&gt;
php &lt;span class=&quot;token parameter variable&quot;&gt;-v&lt;/span&gt;
php &lt;span class=&quot;token parameter variable&quot;&gt;-m&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;grep&lt;/span&gt; imap&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Wenn &lt;code class=&quot;language-text&quot;&gt;imap&lt;/code&gt; in der Ausgabe auftaucht, bist du fertig. Wenn nicht, lies weiter.&lt;/p&gt;
&lt;h2&gt;Brauchst du ext-imap wirklich?&lt;/h2&gt;
&lt;p&gt;Ernst gemeinte Frage. Bevor du dich mit C-Bibliotheken und Compiler-Flags herumschlagst, überleg kurz, ob du die native IMAP-Extension wirklich brauchst.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;PHP 8.4 hat ext-imap aus dem Core entfernt.&lt;/strong&gt; Sie ist zu PECL gewandert, und die zugrunde liegende C-Bibliothek (&lt;code class=&quot;language-text&quot;&gt;libc-client&lt;/code&gt; / UW-IMAP) hat seit 2018 kein Update gesehen. Sie hat Thread-Safety-Probleme, fehlenden XAUTH-Support und POP-Bugs. Sie kommt nicht zurück.&lt;/p&gt;
&lt;p&gt;Die moderne Alternative ist &lt;a href=&quot;https://github.com/Webklex/php-imap&quot;&gt;&lt;strong&gt;Webklex/php-imap&lt;/strong&gt;&lt;/a&gt; -- eine reine PHP-Implementierung von IMAP:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;composer&lt;/span&gt; require webklex/php-imap&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Das war&apos;s. Keine brew-Abhängigkeiten, keine Compiler-Flags, keine Jagd nach Header-Dateien. Es funktioniert mit PHP 8.0.2+ (einschließlich 8.4 und 8.5), unterstützt IMAP IDLE und OAuth und hat über 5 Millionen Installationen auf Packagist. Es gibt auch eine &lt;a href=&quot;https://github.com/Webklex/laravel-imap&quot;&gt;Laravel-Integration&lt;/a&gt;, falls das dein Stack ist.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Verwende ext-imap nur, wenn&lt;/strong&gt; du eine Legacy-Codebasis auf PHP 8.3 oder älter wartest, die bereits von &lt;code class=&quot;language-text&quot;&gt;imap_*&lt;/code&gt;-Funktionen abhängt, und du noch nicht migrieren kannst.&lt;/p&gt;
&lt;h2&gt;Was jeder Schritt macht&lt;/h2&gt;
&lt;p&gt;Wenn der Quick Fix funktioniert hat, kannst du hier aufhören. Wenn nicht -- oder wenn du verstehen willst, was passiert -- hier ist die Aufschlüsselung.&lt;/p&gt;
&lt;h3&gt;Die Brew-Abhängigkeiten&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;brew &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; tidy-html5 openssl@3 zlib &lt;span class=&quot;token function&quot;&gt;bzip2&lt;/span&gt; libedit readline &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
  gettext libiconv libsodium krb5 imap-uw &lt;span class=&quot;token function&quot;&gt;curl&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;macOS bringt einige davon mit, aber phpenv braucht die Homebrew-Versionen mit ordentlichen Headern und pkg-config-Dateien. Entscheidend ist &lt;code class=&quot;language-text&quot;&gt;imap-uw&lt;/code&gt; -- das ist die UW-IMAP-Bibliothek, die &lt;code class=&quot;language-text&quot;&gt;libc-client&lt;/code&gt; bereitstellt.&lt;/p&gt;
&lt;h3&gt;PHP_BUILD_CONFIGURE_OPTS&lt;/h3&gt;
&lt;p&gt;phpenv verwendet unter der Haube php-build, das &lt;code class=&quot;language-text&quot;&gt;./configure&lt;/code&gt; auf dem PHP-Quellcode ausführt. Die Variable &lt;code class=&quot;language-text&quot;&gt;PHP_BUILD_CONFIGURE_OPTS&lt;/code&gt; reicht Flags direkt an configure weiter. Jedes &lt;code class=&quot;language-text&quot;&gt;--with-*&lt;/code&gt;-Flag sagt PHP, wo es eine bestimmte Bibliothek findet.&lt;/p&gt;
&lt;p&gt;Die wichtigsten für IMAP:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;--with-imap=$(brew --prefix imap-uw)&lt;/code&gt; -- zeigt auf die IMAP-Bibliothek&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;--with-imap-ssl=$(brew --prefix openssl@3)&lt;/code&gt; -- aktiviert IMAP über SSL&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;--with-kerberos=$(brew --prefix krb5)&lt;/code&gt; -- für IMAP-Authentifizierung erforderlich&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Der CPPFLAGS-Fix&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token assign-left variable&quot;&gt;CPPFLAGS&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;-I&lt;span class=&quot;token variable&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$(&lt;/span&gt;brew &lt;span class=&quot;token parameter variable&quot;&gt;--prefix&lt;/span&gt; openssl@3&lt;span class=&quot;token variable&quot;&gt;)&lt;/span&gt;&lt;/span&gt;/include -I&lt;span class=&quot;token variable&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$(&lt;/span&gt;brew &lt;span class=&quot;token parameter variable&quot;&gt;--prefix&lt;/span&gt; imap-uw&lt;span class=&quot;token variable&quot;&gt;)&lt;/span&gt;&lt;/span&gt;/include &lt;span class=&quot;token variable&quot;&gt;$CPPFLAGS&lt;/span&gt;&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Selbst mit den configure-Flags findet der C-Präprozessor manchmal die Header-Dateien nicht. Das passiert, weil macOS Homebrew-Header nicht in den Standard-Suchpfad legt. Die zwei Header, die am häufigsten scheitern:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;openssl/ssl.h&lt;/code&gt; -- von OpenSSL&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;imap/imap.h&lt;/code&gt; -- von imap-uw&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Der PATH-Fix&lt;/h3&gt;
&lt;p&gt;Wenn &lt;code class=&quot;language-text&quot;&gt;php -v&lt;/code&gt; nach der Installation die falsche Version zeigt, sind phpenvs Shims nicht in deinem PATH (oder etwas anderes überdeckt sie). Füg das zu &lt;code class=&quot;language-text&quot;&gt;~/.zshrc&lt;/code&gt; hinzu:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token assign-left variable&quot;&gt;PHPENV_ROOT&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token environment constant&quot;&gt;$HOME&lt;/span&gt;/.phpenv&quot;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-d&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;${PHPENV_ROOT}&lt;/span&gt;&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;then&lt;/span&gt;
  &lt;span class=&quot;token builtin class-name&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token assign-left variable&quot;&gt;&lt;span class=&quot;token environment constant&quot;&gt;PATH&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;${PHPENV_ROOT}&lt;/span&gt;/shims:&lt;span class=&quot;token variable&quot;&gt;${PHPENV_ROOT}&lt;/span&gt;/bin:&lt;span class=&quot;token environment constant&quot;&gt;$PATH&lt;/span&gt;&quot;&lt;/span&gt;
  &lt;span class=&quot;token builtin class-name&quot;&gt;eval&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$(&lt;/span&gt;phpenv init -&lt;span class=&quot;token variable&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&quot;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;fi&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Dann &lt;code class=&quot;language-text&quot;&gt;source ~/.zshrc&lt;/code&gt; ausführen und es erneut versuchen.&lt;/p&gt;
&lt;h3&gt;Der utf8_mime2text-Fehler&lt;/h3&gt;
&lt;p&gt;Wenn du das hier siehst:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;configure: error: utf8_mime2text() has new signature, but U8T_CANONICAL is missing.
This should not happen.&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Deine &lt;code class=&quot;language-text&quot;&gt;imap-uw&lt;/code&gt;-Bibliothek ist veraltet oder kaputt. Beheb es mit:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;brew upgrade imap-uw&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Danach die Installation erneut ausführen.&lt;/p&gt;
&lt;h2&gt;Die Zukunft von PHP + IMAP&lt;/h2&gt;
&lt;p&gt;Die Zeichen stehen eindeutig. ext-imap ist deprecated, die zugrunde liegende C-Bibliothek ist verlassen, und PHP 8.4 hat sie bereits aus dem Core entfernt. Wenn du das liest, weil du IMAP in einem PHP-Projekt brauchst, fang an, deine Migration zu &lt;code class=&quot;language-text&quot;&gt;webklex/php-imap&lt;/code&gt; zu planen. Die native Extension lebt auf geborgter Zeit.&lt;/p&gt;
&lt;p&gt;Für diejenigen unter uns, die Legacy-Codebasen warten -- diese Anleitung wird für PHP 8.3 und älter weiter funktionieren. Aber starte keine neuen Projekte mit ext-imap. Es gibt keinen Grund, mit der Kompilierung von C-Bibliotheken zu kämpfen, wenn eine reine PHP-Lösung existiert.&lt;/p&gt;
&lt;hr&gt;
&lt;h3&gt;Referenzen&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href=&quot;https://php.watch/versions/8.4/imap-unbundled&quot;&gt;PHP 8.4: IMAP Extension Unbundled&lt;/a&gt; -- &lt;em&gt;PHP.Watch-Dokumentation zur Entfernung von ext-imap.&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/Webklex/php-imap&quot;&gt;Webklex/php-imap auf GitHub&lt;/a&gt; -- &lt;em&gt;Reine PHP-IMAP-Implementierung (5M+ Packagist-Installationen).&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.php.net/manual/en/book.imap.php&quot;&gt;PHP Manual: IMAP Extension&lt;/a&gt; -- &lt;em&gt;Offizielle Dokumentation mit Deprecation-Hinweisen.&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/shivammathur/homebrew-extensions&quot;&gt;shivammathur/extensions: IMAP for PHP 8.3+&lt;/a&gt; -- &lt;em&gt;Homebrew-Tap für PHP-Extensions einschließlich IMAP.&lt;/em&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h3&gt;Verwandte Beiträge&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;/php-8-5-new-features-pipe-operator-guide/&quot;&gt;PHP 8.5: A Tour of the Incoming Features&lt;/a&gt; -- was als Nächstes in PHP kommt.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/understanding-class-namespace-changes-shopware-6-5-developers-guide/&quot;&gt;Dev Guide: Shopware 6.5/6.6 Class &amp;#x26; Namespace Updates&lt;/a&gt; -- ein weiterer PHP-Migrationsguide für Shopware-Entwickler.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/laravel-sail-vs-laradock-choosing-right-docker-solution/&quot;&gt;Laravel Sail vs Laradock&lt;/a&gt; -- das richtige Docker-Setup für PHP-Entwicklung wählen.&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[Laravel Sail vs. Laradock: Vergleich für PHP-Docker-Devs]]></title><description><![CDATA[TL;DR: Für die meisten Laravel-Entwickler im Jahr 2026: Verwende Laravel Herd, wenn du null Reibung willst (nativ, kein Docker, in Sekunden…]]></description><link>https://bdteo.com/de/laravel-sail-vs-laradock-choosing-right-docker-solution/</link><guid isPermaLink="false">https://bdteo.com/de/laravel-sail-vs-laradock-choosing-right-docker-solution/</guid><pubDate>Thu, 08 Aug 2024 12:00:00 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;TL;DR:&lt;/strong&gt; Für die meisten Laravel-Entwickler im Jahr 2026: Verwende &lt;strong&gt;Laravel Herd&lt;/strong&gt;, wenn du null Reibung willst (nativ, kein Docker, in Sekunden eingerichtet). Verwende &lt;strong&gt;Sail&lt;/strong&gt;, wenn dein Team identische Umgebungen braucht oder du von Diensten wie Redis/Meilisearch abhängst. Verwende &lt;strong&gt;Laradock&lt;/strong&gt;, wenn du mit mehreren PHP-Frameworks arbeitest. Verwende ein &lt;strong&gt;eigenes Docker-Compose&lt;/strong&gt;-Setup, wenn du aus den Abstraktionen herausgewachsen bist. Und wenn Performance alles ist, schau dir &lt;strong&gt;FrankenPHP + Octane&lt;/strong&gt; an.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Ursprünglich im August 2024 veröffentlicht. Im März 2026 mit Laravel 12/Herd/FrankenPHP und dem aktuellen Stand des Ökosystems aktualisiert.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Früher lautete die Frage: &quot;Sail oder Laradock?&quot; Dieser Rahmen ist inzwischen zu eng. Die eigentliche Frage ist: &lt;strong&gt;Wie sollte ich meine Laravel-Entwicklungsumgebung im Jahr 2026 aufsetzen?&lt;/strong&gt; Es gibt mehr Optionen als je zuvor, und die beste Wahl hängt davon ab, was du tatsächlich brauchst.&lt;/p&gt;
&lt;p&gt;Ich habe die meisten davon benutzt. Aktuell nutze ich ein eigenes Docker-Compose-Setup, weil ich volle Kontrolle über meine Container will, ohne dass Abstraktionen verbergen, was passiert. Aber das ist meine Vorliebe, keine universelle Empfehlung. Gehen wir durch, was dir jede Option gibt.&lt;/p&gt;
&lt;h2&gt;Die Kandidaten&lt;/h2&gt;
&lt;h3&gt;Laravel Herd&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://herd.laravel.com/&quot;&gt;Herd&lt;/a&gt; ist die neueste Option und für viele Entwickler die richtige. Es ist eine native Anwendung (macOS und Windows - noch kein Linux), die dir PHP, Nginx, Node.js und Dnsmasq ohne Docker gibt. Die Pro-Version ergänzt MySQL, PostgreSQL, Redis und Debugging-Tools.&lt;/p&gt;
&lt;p&gt;Das Killer-Feature: PHP-Versionen in Sekunden wechseln (7.4 bis 8.4), automatisches &lt;code class=&quot;language-text&quot;&gt;*.test&lt;/code&gt;-Domain-Routing und kein Docker-Overhead. Wenn du eine normale Laravel-App baust und keine exotischen Dienste brauchst, bringt Herd dich in unter einer Minute zum Coden.&lt;/p&gt;
&lt;h3&gt;Laravel Sail&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://laravel.com/docs/12.x/sail&quot;&gt;Sail&lt;/a&gt; ist Laravels offizielle Docker-basierte Entwicklungsumgebung. Es legt eine &lt;code class=&quot;language-text&quot;&gt;sail&lt;/code&gt;-CLI über Docker Compose, die die üblichen Befehle abstrahiert (&lt;code class=&quot;language-text&quot;&gt;sail up&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;sail artisan&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;sail php&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;Seit Laravel 12 wird Sail standardmäßig mit PHP 8.5 ausgeliefert, nutzt &lt;code class=&quot;language-text&quot;&gt;compose.yaml&lt;/code&gt; (den modernen Dateinamen, nicht &lt;code class=&quot;language-text&quot;&gt;docker-compose.yml&lt;/code&gt;) und bringt Swoole für Octane direkt mit. Außerdem unterstützt es Devcontainer-Generierung über &lt;code class=&quot;language-text&quot;&gt;--devcontainer&lt;/code&gt; für die Integration mit VS Code/GitHub Codespaces.&lt;/p&gt;
&lt;p&gt;Standarddienste: PHP, MySQL, Redis, Meilisearch, Mailpit und Selenium.&lt;/p&gt;
&lt;h3&gt;Laradock&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://laradock.io/&quot;&gt;Laradock&lt;/a&gt; ist das Schweizer Taschenmesser. Es ist eine Open-Source-Docker-Umgebung, die jedes PHP-Projekt unterstützt - nicht nur Laravel. Sie bietet 70+ vorkonfigurierte Dienste und kann für den Produktionseinsatz konfiguriert werden.&lt;/p&gt;
&lt;p&gt;Stand Dezember 2025 wird sie weiterhin aktiv gepflegt (aktuelle PHP-FPM- und Workspace-Image-Updates). Der Preis dafür ist Komplexität: Das Setup dauert länger, die Konfiguration erfordert das Bearbeiten mehrerer Dateien, und du brauchst echtes Docker-Wissen.&lt;/p&gt;
&lt;h3&gt;FrankenPHP + Octane&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://frankenphp.dev/&quot;&gt;FrankenPHP&lt;/a&gt; ist ein moderner PHP-Anwendungsserver auf Basis von Caddy. Zusammen mit Laravel Octane erreicht er 4-6 ms Framework-Boot-Zeit pro Request - ein Entwickler berichtete, dass die Latenz durch den Wechsel in den Worker Mode von 7 Sekunden auf 66 ms fiel &lt;small&gt;&lt;a href=&quot;#ref1&quot;&gt;[1]&lt;/a&gt;&lt;/small&gt;.&lt;/p&gt;
&lt;p&gt;Laravel Cloud nutzt FrankenPHP in seiner Octane-Runtime in Produktion &lt;small&gt;&lt;a href=&quot;#ref2&quot;&gt;[2]&lt;/a&gt;&lt;/small&gt;. Das neueste Release (v1.11.2, Februar 2026) brachte durch Go 1.26 30% schnelleres CGO und 40% schnellere Garbage Collection.&lt;/p&gt;
&lt;p&gt;Das ist keine Entwicklungsumgebung im traditionellen Sinn - es ist eine produktionsreife PHP-Runtime, die du auch in der Entwicklung nutzen kannst. Sail enthält eine Integration, um Octane mit FrankenPHP oder Swoole auszuführen.&lt;/p&gt;
&lt;h2&gt;Wann du was nutzen solltest&lt;/h2&gt;
&lt;p&gt;Hier ist meine ehrliche Einschätzung, basierend darauf, diese Werkzeuge tatsächlich benutzt zu haben:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Nutze Herd, wenn&lt;/strong&gt; du allein oder in einem kleinen Team arbeitest, normale Laravel-Apps baust und null Zeit auf Infrastruktur verwenden willst. Es ist der schnellste Weg von &quot;ich habe eine Idee&quot; zu &quot;ich schreibe Code&quot;. Die Einschränkung ist, dass es nur für macOS/Windows verfügbar ist und die kostenlose Version keine Datenbanken enthält.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Nutze Sail, wenn&lt;/strong&gt; dein Team Umgebungsgleichheit braucht, du von bestimmten Dienstversionen abhängst (Redis 7, MySQL 8, PostgreSQL 15), oder du in einer CI/CD-Pipeline arbeitest, die Docker benötigt. Mit Sails &lt;code class=&quot;language-text&quot;&gt;sail:publish&lt;/code&gt;-Befehl kannst du das Docker-Setup anpassen, wenn du aus den Defaults herauswächst.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Nutze Laradock, wenn&lt;/strong&gt; du über mehrere PHP-Frameworks hinweg arbeitest (Symfony, Shopware, Vanilla PHP), exotische Dienste brauchst (Aerospike, RethinkDB, Manticore), oder eine Docker-Umgebung für mehrere Projekte willst. Die Lernkurve ist steiler, aber die Flexibilität ist unerreicht.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Nutze ein eigenes Docker-Compose-Setup, wenn&lt;/strong&gt; du sowohl aus Sail als auch aus Laradock herausgewachsen bist und volle Kontrolle willst. Das ist, was ich mache. Ich pflege mein eigenes &lt;code class=&quot;language-text&quot;&gt;compose.yaml&lt;/code&gt; mit exakt den Diensten, die ich brauche, ohne Abstraktionsschicht, und mit Docker-Compose-Aliases, damit die Befehle kurz bleiben. Es braucht am Anfang mehr Arbeit, aber es gibt keine Magie - alles ist explizit.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Nutze FrankenPHP + Octane, wenn&lt;/strong&gt; du eine High-Performance-API baust oder deine Anwendung latenzsensibel ist. Der Performance-Unterschied ist nicht marginal - es ist eine Größenordnung. Einen Blick wert, selbst wenn du für die allgemeine Entwicklung ein anderes Werkzeug nutzt.&lt;/p&gt;
&lt;h2&gt;Die Details, die zählen&lt;/h2&gt;
&lt;h3&gt;Setup-Zeit&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tool&lt;/th&gt;
&lt;th&gt;Zeit bis zum ersten Request&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Herd&lt;/td&gt;
&lt;td&gt;Unter 1 Minute&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Sail&lt;/td&gt;
&lt;td&gt;5-10 Minuten (Image-Pulls)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Custom Compose&lt;/td&gt;
&lt;td&gt;30-60 Minuten (initiales Setup)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Laradock&lt;/td&gt;
&lt;td&gt;1-2 Stunden (vollständige Konfiguration)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3&gt;Anpassbarkeit&lt;/h3&gt;
&lt;p&gt;Sail ist absichtlich begrenzt. Du bekommst die Dienste, die Laravel braucht, und nicht viel mehr. Du &lt;em&gt;kannst&lt;/em&gt; anpassen, indem du &lt;code class=&quot;language-text&quot;&gt;sail:publish&lt;/code&gt; ausführst und die Dockerfiles bearbeitest, aber an diesem Punkt pflegst du ein eigenes Docker-Setup mit Sails Abstraktionen obendrauf - das Schlechteste aus beiden Welten.&lt;/p&gt;
&lt;p&gt;Laradock gibt dir alles, verlangt aber, dass du verstehst, was du aktivierst. Einen Dienst einzuschalten bedeutet, &lt;code class=&quot;language-text&quot;&gt;.env&lt;/code&gt; und möglicherweise &lt;code class=&quot;language-text&quot;&gt;docker-compose.yml&lt;/code&gt; zu bearbeiten, und manche Dienste haben eigene Konfigurationsverzeichnisse.&lt;/p&gt;
&lt;p&gt;Custom Compose gibt dir genau das, was du schreibst. Nicht mehr, nicht weniger.&lt;/p&gt;
&lt;h3&gt;Produktionsreife&lt;/h3&gt;
&lt;p&gt;Sail ist ausdrücklich nicht für Produktion gedacht. Laradock kann für Produktion konfiguriert werden, aber du musst wissen, was du bei Security Hardening, Resource Limits und sauberem Networking tust. FrankenPHP ist von Grund auf produktionsreif - dafür ist es gebaut.&lt;/p&gt;
&lt;h3&gt;Multi-Projekt-Unterstützung&lt;/h3&gt;
&lt;p&gt;Sail: ein Projekt pro Umgebung. Du kannst mehrere Sail-Instanzen laufen lassen, aber sie werden sich um Ports streiten.&lt;/p&gt;
&lt;p&gt;Laradock: für Multi-Projekt-Setups entworfen. Eine Umgebung, mehrere Projekte, geteilte Dienste.&lt;/p&gt;
&lt;p&gt;Custom Compose: was immer du architektierst. Ich halte separate Compose-Dateien pro Projekt mit gemeinsamen Netzwerkdefinitionen.&lt;/p&gt;
&lt;h2&gt;Was ich tatsächlich nutze&lt;/h2&gt;
&lt;p&gt;Custom Docker Compose. Ich habe Aliases für alles - &lt;code class=&quot;language-text&quot;&gt;dcu&lt;/code&gt; für &lt;code class=&quot;language-text&quot;&gt;docker compose up -d&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;dce&lt;/code&gt; für exec, &lt;code class=&quot;language-text&quot;&gt;dcefpm&lt;/code&gt; für PHP-FPM-Shell-Zugriff und eine &lt;code class=&quot;language-text&quot;&gt;sail&lt;/code&gt;-Funktion, die automatisch den Projekt-Root findet. Das Setup steht in meinen &lt;a href=&quot;/de/docker-compose-major-changes-since-october-2023/&quot;&gt;Notizen zur Docker-Compose-Entwicklung&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Ich habe vor Jahren mit Laradock angefangen, bin zu Sail gewechselt, als es erschien, und bin irgendwann bei einem eigenen Setup gelandet, weil ich genau verstehen wollte, was läuft und warum. Jede Abstraktion versteckt Entscheidungen. Manchmal ist das in Ordnung. Manchmal verursachen diese versteckten Entscheidungen Probleme, die schwer zu debuggen sind, weil du sie nicht sehen kannst.&lt;/p&gt;
&lt;p&gt;Wenn ich heute allerdings ein neues Laravel-Projekt mit einem Team starten würde, dem Docker-Interna egal sind, würde ich Sail nehmen. Und wenn ich jemanden neu in Laravel begleiten würde, würde ich sagen: Installier Herd und fang sofort an, Code zu schreiben.&lt;/p&gt;
&lt;h2&gt;Andere Optionen, die erwähnenswert sind&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href=&quot;https://ddev.com/&quot;&gt;DDEV&lt;/a&gt;&lt;/strong&gt; - Docker-basiert, gute Laravel-Unterstützung, aktive Roadmap für 2026 mit geplanter Gitpod-Integration. Einen Blick wert, wenn du es für andere CMS-Projekte nutzt (WordPress, Drupal).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href=&quot;https://lando.dev/&quot;&gt;Lando&lt;/a&gt;&lt;/strong&gt; - eine weitere Docker-Abstraktionsschicht mit Laravel-Plugin (v1.10.0, Januar 2026). Ähnliche Philosophie wie Sail, aber framework-agnostisch.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Valet&lt;/strong&gt; - der Vorgänger von Herd. Funktioniert immer noch, aber Herd hat es für die meisten Anwendungsfälle abgelöst.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3&gt;Referenzen&lt;/h3&gt;
&lt;p&gt;&lt;a id=&quot;ref1&quot;&gt;&lt;/a&gt;1. &lt;a href=&quot;https://medium.com/@danarcahyaa/setup-and-boost-your-laravel-app-with-frankenphp-worker-mode-c0228f44f71b&quot;&gt;Laravel mit FrankenPHP Worker Mode einrichten und beschleunigen&lt;/a&gt; - &lt;em&gt;Performance-Vergleich aus der Praxis: 7 s auf 66 ms Latenz.&lt;/em&gt;&lt;br&gt;
&lt;a id=&quot;ref2&quot;&gt;&lt;/a&gt;2. &lt;a href=&quot;https://devconf.net/talk/florian-beer-how-laravel-cloud-uses-frankenphp-in-production&quot;&gt;Wie Laravel Cloud FrankenPHP in Produktion nutzt&lt;/a&gt; - &lt;em&gt;DevConf-Talk über Laravel Clouds Octane-Runtime.&lt;/em&gt;&lt;br&gt;
&lt;a id=&quot;ref3&quot;&gt;&lt;/a&gt;3. &lt;a href=&quot;https://laravel.com/docs/12.x/sail&quot;&gt;Laravel 12.x Sail-Dokumentation&lt;/a&gt; - &lt;em&gt;Offizielle Sail-Dokumentation mit PHP 8.5 und compose.yaml-Änderungen.&lt;/em&gt;&lt;br&gt;
&lt;a id=&quot;ref4&quot;&gt;&lt;/a&gt;4. &lt;a href=&quot;https://herd.laravel.com/&quot;&gt;Laravel Herd&lt;/a&gt; - &lt;em&gt;Offizielle Website für die native Laravel-Entwicklungsumgebung.&lt;/em&gt;&lt;br&gt;
&lt;a id=&quot;ref5&quot;&gt;&lt;/a&gt;5. &lt;a href=&quot;https://laravel-news.com/frankenphp-v1112-released-with-30-faster-cgo-40-faster-gc-and-security-patches&quot;&gt;FrankenPHP v1.11.2 Release&lt;/a&gt; - &lt;em&gt;Release vom Februar 2026 mit Performance- und Sicherheitsupdates.&lt;/em&gt;&lt;br&gt;
&lt;a id=&quot;ref6&quot;&gt;&lt;/a&gt;6. &lt;a href=&quot;https://github.com/laradock/laradock&quot;&gt;Laradock auf GitHub&lt;/a&gt; - &lt;em&gt;Weiterhin gepflegt, Updates im Dezember 2025.&lt;/em&gt;&lt;br&gt;
&lt;a id=&quot;ref7&quot;&gt;&lt;/a&gt;7. &lt;a href=&quot;https://aschmelyun.com/blog/the-current-state-of-local-laravel-development/&quot;&gt;Der aktuelle Stand lokaler Laravel-Entwicklung&lt;/a&gt; - &lt;em&gt;Andrew Schmelyuns Überblick über das Ökosystem.&lt;/em&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h3&gt;Ähnliche Beiträge&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;/de/docker-compose-major-changes-since-october-2023/&quot;&gt;Docker-Compose-Entwicklung: Was sich geändert hat und warum es zählt&lt;/a&gt; - die Docker-Compose-Änderungen, die all diese Werkzeuge betreffen.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/de/php-8-5-new-features-pipe-operator-guide/&quot;&gt;PHP 8.5: Ein Rundgang durch die kommenden Features&lt;/a&gt; - was in der PHP-Version kommt, die Laravel 12 standardmäßig nutzt.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/installing-php-8-3-6-with-imap-on-macos-using-phpenv/&quot;&gt;PHP 8.3.6 + IMAP auf macOS mit phpenv&lt;/a&gt; - wenn du ein bestimmtes PHP-Setup außerhalb von Docker brauchst.&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[Der schlimmste Heuchler: Die Geschichte einer Gummiente über Selbstliebe]]></title><description><![CDATA[Von Boris an eine geschätzte Freundin:
~ Die Person, die ich immer schwer verstehen konnte, und doch die verständnisvollste von allen. ~ Hey…]]></description><link>https://bdteo.com/de/worst-hypocrite-rubber-duck-tale/</link><guid isPermaLink="false">https://bdteo.com/de/worst-hypocrite-rubber-duck-tale/</guid><pubDate>Wed, 17 Jul 2024 12:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Von Boris an eine geschätzte Freundin:
~ Die Person, die ich immer schwer verstehen konnte, und doch die verständnisvollste von allen. ~&lt;/p&gt;
&lt;p&gt;Hey du. Hallo nochmal. Können wir uns wieder zum ersten Mal treffen?
Ja. Ich glaube, diesmal wird es cooler. Ich werde kitschiger sein als je zuvor 🧀, versuchen, dich öfter zum Lachen zu bringen, aber manchmal trotzdem scheitern, oder meistens, oder wie auch immer...
Es wird fast so sein wie beim letzten Mal, aber nicht wie beim letzten Mal. Hinweis: Du darfst so tun, als ergäbe mein letzter Satz Sinn, einfach um der Raffinesse willen 🥸&lt;/p&gt;
&lt;p&gt;Lass mich mit einem kleinen Wörterbuch anfangen, oder einer Einleitung, oder einer kleinen Wissensdatenbank:&lt;/p&gt;
&lt;h2&gt;Ein &quot;Anfang&quot; rückwärts ist &quot;gnafnA&quot;&lt;/h2&gt;
&lt;p&gt;Ich brauchte nur einen neuen Titel, damit mein Absatz hübscher wird. Wie auch immer: Neues Ich. Neues Du. Alte Kleidung und ein paar alte schlechte Gewohnheiten. Puh!&lt;/p&gt;
&lt;h2&gt;Ein Spiegel an der Wand&lt;/h2&gt;
&lt;p&gt;Du hast mir sehr gefehlt, und ich habe lange über meine Fehler nachgedacht. Spiegel gibt es schließlich, damit wir hübscher und weniger schmutzig werden.&lt;/p&gt;
&lt;h2&gt;Eine Gummiente&lt;/h2&gt;
&lt;p&gt;Das bin ich. Ich bin eine Gummiente. Ich bin aus Gummi gemacht, und das bedeutet, dass auch mein Kopf aus Gummi gemacht ist.&lt;/p&gt;
&lt;h2&gt;Gummienten &quot;quaken&quot; nicht. Oder doch?&lt;/h2&gt;
&lt;p&gt;In meiner bescheidenen Lebenserfahrung als Gummiente &quot;quake&quot; ich durchaus, und ich tue es ziemlich oft. 😆&lt;/p&gt;
&lt;h3&gt;Eine quakende Gummiente quakt&lt;/h3&gt;
&lt;p&gt;Quak! Quak! Quak! - sagte die Ente und quakte wieder. Erstaunlicher Klang! Nicht wahr?&lt;/p&gt;
&lt;h3&gt;Eine Gummiente mit Haltung&lt;/h3&gt;
&lt;p&gt;Ich bin die freundlichste Gummiente von allen, sagte die Gummiente und quakte aufgeregt! Quak!&lt;/p&gt;
&lt;h3&gt;Der wahre Untergang der Gummienten&lt;/h3&gt;
&lt;p&gt;Quaken kann ziemlich erschöpfend sein, weißt du. Man kann mit einem frischen Atemzug nur eine begrenzte Anzahl von Malen schön quaken.
Aber man kann trotzdem versuchen, schön zu quaken, wenn man leer ist? Richtig?
Natürlich kann man das! Man kann versuchen zu quaken! Man kann sogar das beste schnelle Quak aus seinem Gummi geben, um den anderen leeren Gummienten zu helfen, einen Atemzug zu holen.&lt;/p&gt;
&lt;h2&gt;Eine kurze Geschichte für zwei halb leere Gummienten&lt;/h2&gt;
&lt;p&gt;&quot;Weißt du was?&quot; - sagte Duckitty
&quot;Was?&quot; - sagte Duckelton
&quot;Wenn ich zu viel quake, kann ich nicht mehr quaken, und ich will quaken, und das macht mich traurig.&quot; - antwortete Duckitty
Duckleton setzte einen sehr ernsten Blick auf, runzelte die Augenbrauen und sagte mit weiser Stimme:
&quot;Ja. Mir geht es genauso!&quot;&lt;/p&gt;
&lt;h2&gt;Die Gummiente, die es hasste, still zu sein&lt;/h2&gt;
&lt;p&gt;Duckleton hasste es, still zu sein, und gab ein lautes Quaken von sich, wann immer er konnte. Manchmal war er ziemlich leer und fragte sich, ob er gelb genug war, um zu quaken.
Aber weißt du, die Fabrik, die Ducleton und Duckitty gemacht hatte, hatte sie beide perfekt gelb gemacht.
Duckleton war eine Gummiente mit Haltung. Er dachte, er könne und müsse jede andere Ente überquaken, um sie vor der Scham zu retten, nicht zu quaken, wenn sie leer waren.
Duckitty hatte manchmal ähnliche Gedanken, oder zumindest dachte Duckleton so, wenn er bei klarem Verstand war. (Manchmal war er das nicht 😆)
Aber Duckleton war eine stolze Gummiente. Eine Gummiente aus der besten Fabrik der Welt, die seit Jahrhunderten die besten quakenden gelben Gummienten herstellte. Zumindest dachte Duckleton so, bis er Duckitty traf.
Als er sie traf, war er so erstaunt von ihrer Stimme, dass er dachte, sie müsse in irgendeiner außerirdischen Fabrik gemacht worden sein, viel besser als die Erdenfabrik, aus der er kam.
Er war glücklich, die Chance zu haben, diese wunderbare und perfekte Gummiente namens Duckitty einfach zu bewundern, die viel besser war als er (dachte er).
&quot;Nun, es ist so schön, sie anzuschauen. Sie ist so gelb. Sie ist so nett. Sie hat eine viel hübschere Stimme als ich. Ich bin eine glückliche Gummiente.&quot; - dachte Duckleton.&lt;/p&gt;
&lt;h2&gt;Eine kurze, aber ein bisschen traurige Geschichte&lt;/h2&gt;
&lt;p&gt;Duckleton und Duckitty waren beide glücklich und quakten, als wäre es der beste Quak-Wettbewerb der Welt und des Weltalls.
Einmal kam ein Menschenkind und drückte Duckleton zusammen. Er konnte eine Zeit lang nicht quaken, wollte es aber immer noch unbedingt, also blieb er still, ohne sich zu beschweren, holte tief Luft, und beide quakten glücklich weiter.
Einige Zeit verging, und Duckitty wurde ebenfalls von einem fiesen Menschenkind genommen. Das Menschenkind drückte die ganze Luft aus Duckitty heraus, aber Duckleton war zu sehr mit Quaken beschäftigt, um es zu bemerken.
Duckitty war in Ordnung, aber sie musste etwas Luft holen.
Duckleton war traurig bis über jede Verzweiflung hinaus. Er dachte, seine Lieblingsente sei kaputt und er sei wieder für immer allein bis ans Ende des Universums. Er hatte nur vergessen, dass dasselbe Kind ihn vor einiger Zeit zusammengedrückt hatte.
Duckleton war stolz, aber dumm; schließlich hatte er einen kleinen Gummikopf. Als Duckitty quakte, war Duckleton wütend. Er war zugleich froh, dass sie lebte, aber auch ein bisschen entmutigt, weil er dachte, Duckitty könne das Zusammendrücken durch ein Menschenkind besser überstehen als er.
Sie war doch so viel perfekter als er, oder?&lt;/p&gt;
&lt;h2&gt;Duckletons Schuldreise&lt;/h2&gt;
&lt;p&gt;Duckleton war der schlimmste Heuchler von allen, und er lernte es endlich. Er war traurig, aber er dachte: Lass uns bei Duckitty entschuldigen und ihr etwas Dankbarkeit dafür zeigen, dass sie so perfekt ist, wie sie ist.&lt;/p&gt;
&lt;h2&gt;Epilog&lt;/h2&gt;
&lt;p&gt;Meine liebe Freundin, du warst in den letzten 8 Jahren perfekt. Du warst jeden einzelnen Tag perfekt. Es tut mir wirklich leid, dass ich so ein Heuchler war, aber schlimme Dinge passieren nun mal, weißt du. Und wir machen trotzdem weiter.
Bitte liebe dich selbst viel mehr. Ich bin dumm. Du bist klug. Oder wie auch immer... Aber liebe dich selbst viel mehr, denn du bist perfekt, so wie du bist!
Tatsächlich bist du die perfekteste gelbe Gummiente der Welt. Zumindest denke ich das. Mein Gummikopf ist klein. Aber yay! Ich kann trotzdem denken. 🫶
Quak!&lt;/p&gt;
&lt;p&gt;Quak, Quak, Quak
~ Duckleton&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Diskrete Repräsentationen in RL: Forschungseinblicke von Edan Meyer]]></title><description><![CDATA[Hast du dich je gefragt, wie KI-Agenten lernen, komplexe Umgebungen zu verstehen und mit ihnen zu interagieren? Edan Meyer, ein Forscher im…]]></description><link>https://bdteo.com/de/discrete-representations-reinforcement-learning-insights/</link><guid isPermaLink="false">https://bdteo.com/de/discrete-representations-reinforcement-learning-insights/</guid><pubDate>Mon, 15 Jul 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Hast du dich je gefragt, wie KI-Agenten lernen, komplexe Umgebungen zu verstehen und mit ihnen zu interagieren? Edan Meyer, ein Forscher im Bereich Reinforcement Learning (RL), untersucht einen spannenden Ansatz, der durchaus verändern könnte, wie wir über KI-Lernen nachdenken. Tauchen wir ein in seine faszinierende Arbeit zu diskreten Repräsentationen in RL.&lt;/p&gt;
&lt;h2&gt;Die Macht der Repräsentation&lt;/h2&gt;
&lt;p&gt;Stell dir vor, du willst einem Computer beibringen, ein Videospiel zu spielen. Wie würdest du den Zustand des Spiels so darstellen, dass der Computer ihn verstehen und daraus lernen kann? Genau hier kommt Representation Learning ins Spiel, und es ist ein entscheidender Bestandteil beim Bau wirksamer KI-Agenten.&lt;/p&gt;
&lt;p&gt;Edan Meyer, dessen Arbeit du auf seinem &lt;a href=&quot;https://www.youtube.com/@EdanMeyer&quot;&gt;YouTube-Kanal&lt;/a&gt; verfolgen kannst, beschäftigt sich mit einer bestimmten Art von Repräsentation: diskreten Repräsentationen. Seine Forschung, beschrieben in einem &lt;a href=&quot;https://arxiv.org/abs/2312.01203&quot;&gt;Paper auf arXiv&lt;/a&gt;, zeigt, warum solche Repräsentationen in bestimmten RL-Szenarien besonders nützlich sein können.&lt;/p&gt;
&lt;h2&gt;Zwei Jahre Forschung in 13 Minuten&lt;/h2&gt;
&lt;p&gt;Edan hat zwei Jahre seiner Masterforschung in ein zugängliches 13-minütiges Video mit dem Titel &lt;a href=&quot;https://www.youtube.com/watch?v=s8RqGlU5HEs&quot;&gt;&quot;2 Years of My Research Explained in 13 Minutes&quot;&lt;/a&gt; verdichtet. Darin zerlegt er komplexe Konzepte in verständliche Erklärungen und macht seine Arbeit so einem breiteren Publikum zugänglich.&lt;/p&gt;
&lt;p&gt;Wie Edan in der Videobeschreibung schreibt:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&quot;This is my research into representation learning and model learning in the reinforcement learning setting. Two years in the making, and I finally get to talk about my Master&apos;s research! The paper has been accepted to the Reinforcement Learning Conference (RLC) 2024.&quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Dieses Video ist ein guter Einstieg für alle, die die Grundlagen seiner Forschung verstehen möchten, ohne direkt in das vollständige akademische Paper einzutauchen.&lt;/p&gt;
&lt;h2&gt;Was sind diskrete Repräsentationen?&lt;/h2&gt;
&lt;p&gt;Traditionell verwenden viele RL-Systeme kontinuierliche Repräsentationen. Man kann sie sich als Vektoren aus Dezimalzahlen vorstellen, die jeden möglichen Wert annehmen können. Diskrete Repräsentationen dagegen ähneln eher einer Reihe von Multiple-Choice-Fragen. Jeder &quot;Slot&quot; in der Repräsentation kann nur einen Wert aus einer festen Menge von Möglichkeiten annehmen.&lt;/p&gt;
&lt;p&gt;Wie Edan in seinem Video erklärt, wirkt das zunächst vielleicht einschränkend. Schließlich kann ein kontinuierlicher Wert unendlich viele Zustände darstellen, während ein diskreter Wert deutlich stärker begrenzt ist. Warum also überhaupt diskrete Repräsentationen verwenden?&lt;/p&gt;
&lt;h2&gt;Die überraschenden Vorteile&lt;/h2&gt;
&lt;p&gt;Edans Forschung hat einige faszinierende Vorteile diskreter Repräsentationen sichtbar gemacht:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Bessere Weltmodelle mit weniger Kapazität&lt;/strong&gt;: Wenn eine KI versucht, ein Modell ihrer Umgebung zu lernen, ein sogenanntes &quot;world model&quot;, erlauben diskrete Repräsentationen ihr, mit weniger Rechenleistung genauere Informationen zu erfassen. Das gilt besonders dann, wenn das Modell nicht genug Kapazität hat, um alles an der Umgebung perfekt abzubilden, ein häufiges Szenario bei komplexen Problemen in der realen Welt.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Schnellere Anpassung&lt;/strong&gt;: In Experimenten, in denen sich die Umgebung im Laufe der Zeit veränderte, konnten Agenten mit diskreten Repräsentationen sich schneller an diese Veränderungen anpassen. Das könnte entscheidend für KI-Systeme sein, die in dynamischen, unvorhersehbaren Umgebungen funktionieren müssen.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Effizientes Lernen&lt;/strong&gt;: Diskrete Repräsentationen brauchen anfangs möglicherweise länger, bis sie gelernt sind. Sobald sie aber etabliert sind, ermöglichen sie schnelleres Lernen und schnellere Anpassung, sowohl beim Lernen von Weltmodellen als auch bei Policy-Learning-Aufgaben.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;Warum ist das wichtig?&lt;/h2&gt;
&lt;p&gt;Die Auswirkungen von Edans Arbeit reichen weit über einfache Grid-World-Experimente hinaus. Wie er in seinem Video betont, ist die reale Welt ungleich komplexer als jede Simulation, die wir bauen können. In solchen Umgebungen ist es für eine KI unmöglich, alles zu lernen. Der Schlüssel liegt in Anpassung.&lt;/p&gt;
&lt;p&gt;Diskrete Repräsentationen scheinen ein starkes Werkzeug zu sein, um KI-Systeme zu bauen, die sich schnell auf neue Situationen einstellen können, selbst wenn sie unmöglich jeden Aspekt ihrer Umgebung modellieren können. Das könnte für Anwendungen von Robotik über komplexe Strategiespiele bis weit darüber hinaus ein Wendepunkt sein.&lt;/p&gt;
&lt;h2&gt;Tiefer eintauchen&lt;/h2&gt;
&lt;p&gt;Für alle, die sich für die technischen Details interessieren, untersucht Edans Paper faszinierende Aspekte davon, warum diskrete Repräsentationen so gut funktionieren. Er fand zum Beispiel heraus, dass nicht alle diskreten Repräsentationen gleich sind. Faktoren wie Sparsity und Binarität spielen eine wichtige Rolle für ihre Wirksamkeit.&lt;/p&gt;
&lt;h2&gt;Fazit&lt;/h2&gt;
&lt;p&gt;Edan Meyers Arbeit zu diskreten Repräsentationen im Reinforcement Learning bietet spannende Einblicke darin, wie wir anpassungsfähigere und effizientere KI-Systeme entwickeln könnten. Indem seine Forschung gängige Annahmen darüber infrage stellt, wie Informationen für KI repräsentiert werden sollten, eröffnet sie neue Möglichkeiten für Agenten, die in komplexen, dynamischen Umgebungen bestehen können.&lt;/p&gt;
&lt;p&gt;Ob du KI-Forscher bist, Machine Learning studierst oder dich einfach für die Grenzen der Technologie interessierst: Edans Arbeit bietet einen überzeugenden Blick auf die Zukunft künstlicher Intelligenz. Schau dir unbedingt seinen &lt;a href=&quot;https://www.youtube.com/@EdanMeyer&quot;&gt;YouTube-Kanal&lt;/a&gt;, sein erklärendes &lt;a href=&quot;https://www.youtube.com/watch?v=s8RqGlU5HEs&quot;&gt;Video&lt;/a&gt; und sein &lt;a href=&quot;https://arxiv.org/abs/2312.01203&quot;&gt;Paper&lt;/a&gt; an, wenn du diese Ideen gründlicher erkunden möchtest.&lt;/p&gt;
&lt;p&gt;Denk daran: In der schnelllebigen Welt der KI-Forschung können die experimentellen Techniken von heute die Durchbruchstechnologien von morgen sein. Diskrete Repräsentationen könnten schon bald der Schlüssel sein, um leistungsfähigere und anpassungsfähigere KI-Systeme möglich zu machen.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Googles KI-Geschichte: Versprechen, Aktienentwicklung und Wirkung]]></title><description><![CDATA[Googles Weg durch die Entwicklung und Vermarktung von KI-Technologien wie der Gemini-Serie ist
eine aufschlussreiche Fallstudie über das…]]></description><link>https://bdteo.com/de/google-ai-ambitions-historical-analysis-promises-stock-market-impact/</link><guid isPermaLink="false">https://bdteo.com/de/google-ai-ambitions-historical-analysis-promises-stock-market-impact/</guid><pubDate>Sat, 11 May 2024 12:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Googles Weg durch die Entwicklung und Vermarktung von KI-Technologien wie der Gemini-Serie ist
eine aufschlussreiche Fallstudie über das Zusammenspiel von Unternehmensversprechen,
Börsenentwicklung und technologischer Innovation. Diese Analyse betrachtet konkrete historische
Momente, in denen Googles KI-Versprechen die Aktienkurse des Unternehmens spürbar beeinflusst
haben, und ordnet zugleich Erfolge und Rückschläge ein.&lt;/p&gt;
&lt;p&gt;Der Vorstoß des Technologiekonzerns in die KI war von ehrgeizigen Projekten und selbstbewussten
Aussagen geprägt. Von der Einführung von TensorFlow im Jahr 2015, die Google als führendes
Unternehmen in KI-Forschung und -Entwicklung etablierte, bis zum Start von Google Assistant im
Jahr 2016, der die Wettbewerbsfähigkeit gegenüber Rivalen wie Amazons Alexa und Apples Siri
stärkte, hat Google immer wieder versucht, die Grenzen dessen zu verschieben, was KI leisten kann
&lt;small&gt;&lt;a href=&quot;#ref1&quot;&gt;[1]&lt;/a&gt;&lt;/small&gt;.&lt;/p&gt;
&lt;h3&gt;Zentrale historische Meilensteine in Googles KI-Entwicklung&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Einführung von TensorFlow (2015)&lt;/strong&gt;: Google veröffentlichte TensorFlow, sein Framework für
maschinelles Lernen, als Open Source. Es wurde schnell sehr populär. Dieser Schritt half, Google
als führendes Unternehmen in KI-Forschung und -Entwicklung zu positionieren, und wirkte sich
positiv auf die Marktwahrnehmung aus &lt;small&gt;&lt;a href=&quot;#ref1&quot;&gt;[1]&lt;/a&gt;&lt;/small&gt;. TensorFlow wurde in
unterschiedlichsten Anwendungen eingesetzt, von der Verbesserung von Suchergebnissen bis zum
Betrieb selbstfahrender Autos.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Start von Google Assistant (2016)&lt;/strong&gt;: Die Einführung von Google Assistant stärkte Googles
Wettbewerbsfähigkeit in der KI gegenüber Konkurrenten wie Amazons Alexa und Apples Siri. Der
Markt nahm dies positiv auf, weil es Wachstumspotenzial bei KI-gestützten
Benutzeroberflächen zeigte &lt;small&gt;&lt;a href=&quot;#ref2&quot;&gt;[2]&lt;/a&gt;&lt;/small&gt;. Google Assistant wurde in Smartphones,
Smart-Home-Geräte und Autos integriert und wurde damit zu einem wichtigen Akteur im Markt für
Sprachassistenten.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Fortschritte im Quantencomputing (2019)&lt;/strong&gt;: Google verkündete einen Durchbruch bei der
Quantenüberlegenheit und behauptete, der eigene Quantencomputer könne Berechnungen ausführen,
die außerhalb der Möglichkeiten klassischer Supercomputer liegen. Diese Ankündigung führte zu
einem kurzen Anstieg des Aktienkurses und zeigte die Begeisterung der Investoren für Googles
technologische Spitzenfähigkeiten &lt;small&gt;&lt;a href=&quot;#ref3&quot;&gt;[3]&lt;/a&gt;&lt;/small&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;Aktienmarktentwicklung und KI-Meilensteine&lt;/h3&gt;
&lt;p&gt;Der Aktienmarkt hat unterschiedlich auf Googles KI-Entwicklungen reagiert. Bedeutende
Ankündigungen, etwa Durchbrüche im Quantencomputing oder neue KI-Produkte, führen typischerweise
zu kurzfristigen Kursanstiegen. Langfristig hing die Wirkung auf den Aktienkurs jedoch enger damit
zusammen, ob diese Technologien tatsächlich eingesetzt wurden und kommerziell erfolgreich waren.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Meilensteine des S&amp;#x26;P 500&lt;/strong&gt;: Googles große KI-Ankündigungen fielen oft mit breiteren
Markttrends zusammen. So erreichte Google im breiten Bullenmarkt des Jahres 2021 neue
Aktienkurshöchststände, begleitet von erheblichen Fortschritten im Bereich KI. Das spiegelte ein
starkes Anlegervertrauen in technologiegetriebenes Wachstum wider &lt;small&gt;&lt;a href=&quot;#ref4&quot;&gt;[4]&lt;/a&gt;&lt;/small&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;figure&gt;
  &lt;span class=&quot;gatsby-resp-image-wrapper&quot; style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;&gt;
      &lt;a class=&quot;gatsby-resp-image-link&quot; href=&quot;https://bdteo.com/static/ab6f0e497ea3ee100966d0f875eab63b/56fb7/google_stock_milestones.png&quot; style=&quot;display: block&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;
    &lt;span class=&quot;gatsby-resp-image-background-image&quot; style=&quot;padding-bottom: 49.36708860759494%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAKCAYAAAC0VX7mAAAACXBIWXMAAC4jAAAuIwF4pT92AAABh0lEQVR42l2SyW7jQAxE9f/fNqc55RDADgwjE41kLb1vqhQpSxOPgAYFNvm6uHQhBMzzgr7vsSwLSinIOatNKWH422MYB8bMGMeRdkKMmTFFY8oRSxtiQifAYRhxuVxwv99hjNGA1hq2bUPLCbkkyCc++VKq/N+Qa4NPWX2TSzAuoPM+nIBaq6oSK0d8JUbEFM77bWtUUsEf3B4Ov2+TPvzrOsKS1TnnFChOVfRUtifTUmHiOXwkIVNhylVVvX0u+Bgt+tWjktM9Ho9/5Z0qfgDTK1AU1tJgQlbg6pOqKyxf+tlN03Qm/w9stEWBUUusck87mYT3L4NIlbVV2LS3TIapQzl6+BO46QD4CIGRwMDkG0u7DgbXL4vBRL0/Hn4B1lr0EtpHQjlBx9Vw7NU4W/yZDWafYWVdCIhRhvba8xMoU/UhYnURs5UTsLAvC1dgJWQ1FiH4vWQdHqccZPeeW/Cs7tjbTujOe67GvpjWeQZQBSGNajJXRoCyXt57TRa7Lz9zQnjxfQN54Q2UCayA8gAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;&gt;&lt;/span&gt;
  &lt;img class=&quot;gatsby-resp-image-image&quot; alt=&quot;Google-Aktienmeilensteine&quot; title=&quot;&quot; src=&quot;https://bdteo.com/static/ab6f0e497ea3ee100966d0f875eab63b/f058b/google_stock_milestones.png&quot; srcset=&quot;https://bdteo.com/static/ab6f0e497ea3ee100966d0f875eab63b/c26ae/google_stock_milestones.png 158w,
https://bdteo.com/static/ab6f0e497ea3ee100966d0f875eab63b/6bdcf/google_stock_milestones.png 315w,
https://bdteo.com/static/ab6f0e497ea3ee100966d0f875eab63b/f058b/google_stock_milestones.png 630w,
https://bdteo.com/static/ab6f0e497ea3ee100966d0f875eab63b/40601/google_stock_milestones.png 945w,
https://bdteo.com/static/ab6f0e497ea3ee100966d0f875eab63b/78612/google_stock_milestones.png 1260w,
https://bdteo.com/static/ab6f0e497ea3ee100966d0f875eab63b/56fb7/google_stock_milestones.png 4169w&quot; sizes=&quot;(max-width: 630px) 100vw, 630px&quot; style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;&gt;
  &lt;/a&gt;
    &lt;/span&gt;
  &lt;figcaption&gt;
    Abb. 1 - Google-Aktienmeilensteine.
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h3&gt;Erfolge und Rückschläge&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Erfolge&lt;/strong&gt;: Googles KI hat in Bereichen wie Sprachübersetzung, Bilderkennung und autonomem
Fahren durch Waymo beträchtliche Erfolge erzielt. Diese Erfolge haben dazu beigetragen, Googles
Ruf als technologischer Innovator zu festigen &lt;small&gt;&lt;a href=&quot;#ref5&quot;&gt;[5]&lt;/a&gt;&lt;/small&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Rückschläge&lt;/strong&gt;: Nicht alle KI-Initiativen von Google erfüllten die Erwartungen des Marktes.
Das mit großen Hoffnungen verbundene Projekt Google Glass fand beispielsweise keinen wirklichen
Anklang bei Verbrauchern und wurde eingestellt. Das Produkt stieß auf Datenschutzbedenken und
lieferte für durchschnittliche Nutzer keinen überzeugenden Anwendungsfall. Ähnlich haben die
Verzögerungen und zu großen Versprechen rund um Gemini 1.5 Pro zu Unzufriedenheit und Skepsis
bei Nutzern geführt &lt;small&gt;&lt;a href=&quot;#ref6&quot;&gt;[6]&lt;/a&gt;&lt;/small&gt;. Google hat aus diesen Rückschlägen gelernt,
konzentriert sich stärker auf pragmatische KI-Anwendungen und verbessert die Kommunikation mit
den Nutzern.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;figure&gt;
  &lt;span class=&quot;gatsby-resp-image-wrapper&quot; style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;&gt;
      &lt;a class=&quot;gatsby-resp-image-link&quot; href=&quot;https://bdteo.com/static/74c512a4ea459157d8319ebeced56698/f154a/tech_companies_ai_milestones.png&quot; style=&quot;display: block&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;
    &lt;span class=&quot;gatsby-resp-image-background-image&quot; style=&quot;padding-bottom: 55.69620253164557%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAYAAAB/Ca1DAAAACXBIWXMAAC4jAAAuIwF4pT92AAABuklEQVR42n2S65LaMAyFef/X21+Flg67O4QEkjg4iW35cnrkANPZndaDR8iRPx3J2uWcMQwjzuczxnFETgl2tuiHgf5E22Pg+UjfMK7vb9wDpskgBGF8RIwRwYXq70QEl7bF4bDH6XRCc27gxaMUgLHI/JPU4S/HjJwjEpPqUc56nqAfR7PA+fAAXhrs93tCD+gI98HxUoZIRuRlYRWZfpJENYGkhF+dxZGbDuMjfrwP8E/gfLcsW8uYEEMgRDZl6bvCUlieRDTG4Wdrq8Jjd8fvxtREOwUVvfBYhf0QqkhUFWPZLHdheVFSBfZ2RSD8eLU4XAyG2SN5YTx7eL1eK1AfR+3/gFsPBc24sIpSz98+etysA2UzIYHGmA32D+DfJUtI+BzuaPkAuvRRHEESEzL7VxUG7RkhFfYAauMVpMBpDWipoLcen7cZH/30it+ExGqT+wJ8rsiMi/e4O8HtHtBNDh2Bgw18xQiv6uvYfAE+e6gwla1KppUQ9qczFitHwQf2rORXyTo2Kcl3hUzwUqjZ5mXB7DwhgkB13q+8GOvkr84RHDDPC9aFrxs8dNS0Ms/YRIj6jt/V/gFlSFmPIFDCcAAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;&gt;&lt;/span&gt;
  &lt;img class=&quot;gatsby-resp-image-image&quot; alt=&quot;KI-Meilensteine von Technologieunternehmen&quot; title=&quot;&quot; src=&quot;https://bdteo.com/static/74c512a4ea459157d8319ebeced56698/f058b/tech_companies_ai_milestones.png&quot; srcset=&quot;https://bdteo.com/static/74c512a4ea459157d8319ebeced56698/c26ae/tech_companies_ai_milestones.png 158w,
https://bdteo.com/static/74c512a4ea459157d8319ebeced56698/6bdcf/tech_companies_ai_milestones.png 315w,
https://bdteo.com/static/74c512a4ea459157d8319ebeced56698/f058b/tech_companies_ai_milestones.png 630w,
https://bdteo.com/static/74c512a4ea459157d8319ebeced56698/40601/tech_companies_ai_milestones.png 945w,
https://bdteo.com/static/74c512a4ea459157d8319ebeced56698/78612/tech_companies_ai_milestones.png 1260w,
https://bdteo.com/static/74c512a4ea459157d8319ebeced56698/f154a/tech_companies_ai_milestones.png 4769w&quot; sizes=&quot;(max-width: 630px) 100vw, 630px&quot; style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;&gt;
  &lt;/a&gt;
    &lt;/span&gt;
  &lt;figcaption&gt;Abb. 2 - KI-Meilensteine von Technologieunternehmen.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h3&gt;Marktwirkung und künftiger Ausblick&lt;/h3&gt;
&lt;p&gt;Die Marktwirkung von Googles KI war erheblich. Sie beeinflusste nicht nur die eigenen Aktienkurse,
sondern auch die Richtung der gesamten Technologiebranche. Der KI-Sektor bleibt ein zentrales
Thema für Investoren, wie sich auch an der Entwicklung des S&amp;#x26;P 500 zeigt, in dem Technologiewerte
eine große Rolle spielen &lt;small&gt;&lt;a href=&quot;#ref4&quot;&gt;[4]&lt;/a&gt;&lt;/small&gt;.&lt;/p&gt;
&lt;p&gt;Mit Blick nach vorn wird entscheidend sein, ob Google seine KI-Versprechen einlösen und die
Herausforderungen der technologischen Einführung meistern kann. Die Strategie des Unternehmens,
Transparenz zu erhöhen und Verbrauchererwartungen wirksamer zu steuern, könnte über seine künftige
Marktposition und das Vertrauen der Investoren entscheiden.&lt;/p&gt;
&lt;p&gt;Ein besonders wichtiger Bereich ist Googles Fortschritt mit der Gemini-Serie von KI-Modellen.
Gemini 1.5 Pro, im Februar 2024 angekündigt, verspricht einen deutlichen Sprung bei den
KI-Fähigkeiten, hatte beim Rollout aber mit Schwierigkeiten zu kämpfen &lt;small&gt;&lt;a href=&quot;#ref6&quot;&gt;[6]&lt;/a&gt;&lt;/small&gt;.
Wie Google diese Herausforderungen bewältigt und seine Versprechen einlöst, dürfte deutliche
Auswirkungen auf den KI-Ruf und die Marktstellung des Unternehmens haben.&lt;/p&gt;
&lt;p&gt;Googles ehrgeizige Fortschritte in der künstlichen Intelligenz, besonders durch die Gemini-Serie,
stehen für eine transformative Phase der KI-Technologie. Die Einführung von Gemini 1.5 Pro als
Teil dieser Serie unterstreicht Googles Anspruch, die Grenzen von KI-Fähigkeiten weiter
auszudehnen. Der Rollout von Gemini 1.5 Pro war jedoch nicht frei von Problemen, und gerade diese
sind wichtig, um sowohl das Potenzial als auch die Grenzen solcher fortgeschrittenen KI-Modelle zu
verstehen.&lt;/p&gt;
&lt;h3&gt;Expertenmeinungen zu Gemini 1.5 Pro&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Leistung und Fähigkeiten&lt;/strong&gt;: Gemini 1.5 Pro basiert auf einer Mixture-of-Experts-Architektur
(MoE) und bietet deutliche Verbesserungen gegenüber früheren Modellen, darunter eine massive
Vergrößerung des Kontextfensters auf bis zu 10 Millionen Tokens. Diese Verbesserung ermöglicht
eine bessere Bearbeitung komplexer Aufgaben mit großen Datenmengen über verschiedene Formate
hinweg - Text, Code, Bild und Audio &lt;small&gt;&lt;a href=&quot;#ref7&quot;&gt;[7]&lt;/a&gt;&lt;/small&gt;. Stephen Oladele von Encord
zufolge behält &quot;Gemini 1.5 Pro über den gesamten Kontext hinweg eine nahezu perfekte Erinnerung
bei und nutzt eine Mixture-of-Experts-Architektur für effizienteres Training und hochwertigere
Antworten&quot; &lt;small&gt;&lt;a href=&quot;#ref7&quot;&gt;[7]&lt;/a&gt;&lt;/small&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Herausforderungen beim Rollout&lt;/strong&gt;: Trotz seiner fortgeschrittenen Fähigkeiten war der Rollout von
Gemini 1.5 Pro mit mehreren Herausforderungen verbunden. Das Modell befindet sich derzeit in einer
privaten Vorschauphase, und die allgemeine Verfügbarkeit ist für später vorgesehen, was auf einen
phasenweisen Einführungsansatz hindeutet &lt;small&gt;&lt;a href=&quot;#ref7&quot;&gt;[7]&lt;/a&gt;&lt;/small&gt;. Diese vorsichtige
Rollout-Strategie könnte darauf zurückgehen, dass das Modell weiter feinabgestimmt werden muss und
die hohen Erwartungen seiner Vorgänger sowie des Marktes erfüllen soll.&lt;/p&gt;
&lt;h3&gt;Sichtweisen von Branchenanalysten&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Auswirkungen auf die KI-Branche&lt;/strong&gt;: Branchenanalysten sehen die Gemini-Serie als bedeutenden
Schritt nach vorn für Google, der möglicherweise neue Maßstäbe dafür setzt, was KI-Modelle leisten
können. Die Gemini-Serie, insbesondere 1.5 Pro, dürfte Googles Wettbewerbsfähigkeit gegenüber
anderen Technologiekonzernen wie OpenAI und Microsoft stärken, die ihre eigenen KI-Fähigkeiten
ebenfalls aggressiv ausbauen &lt;small&gt;&lt;a href=&quot;#ref8&quot;&gt;[8]&lt;/a&gt;&lt;/small&gt; &lt;small&gt;&lt;a href=&quot;#ref7&quot;&gt;[7]&lt;/a&gt;&lt;/small&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Marktimplikationen&lt;/strong&gt;: Die Fortschritte in Gemini 1.5 Pro dürften verschiedene Sektoren
beeinflussen, darunter Gesundheitswesen, Finanzwesen und weitere Bereiche, weil sie
anspruchsvollere KI-Anwendungen ermöglichen. Dies könnte zu Verschiebungen in der Marktdynamik
führen: Unternehmen, die solche fortgeschrittenen KI-Technologien effektiv integrieren, können
einen deutlichen Wettbewerbsvorteil gewinnen &lt;small&gt;&lt;a href=&quot;#ref7&quot;&gt;[7]&lt;/a&gt;&lt;/small&gt;.&lt;/p&gt;
&lt;h3&gt;Einblicke von Google-Führungskräften&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Zukunftspläne und ethische Überlegungen&lt;/strong&gt;: Führungskräfte von Google, darunter Sundar Pichai,
haben das Bekenntnis des Unternehmens zu verantwortungsvoller KI-Entwicklung betont. Pichai hat
hervorgehoben, wie wichtig es ist, KI-Fortschritte an ethischen Leitlinien auszurichten und
sicherzustellen, dass diese Technologien zum gesellschaftlichen Nutzen eingesetzt werden
&lt;small&gt;&lt;a href=&quot;#ref2&quot;&gt;[2]&lt;/a&gt;&lt;/small&gt; &lt;small&gt;&lt;a href=&quot;#ref9&quot;&gt;[9]&lt;/a&gt;&lt;/small&gt;. Dieser Ansatz ist entscheidend,
da KI immer leistungsfähiger wird und zunehmend in alltägliche Anwendungen einzieht.&lt;/p&gt;
&lt;h3&gt;Herausforderungen und Chancen navigieren&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Technische Herausforderungen angehen&lt;/strong&gt;: Um die kommenden Herausforderungen zu meistern, muss
Google weiterhin in Forschung und Entwicklung investieren und Themen wie Modellzuverlässigkeit und
ethische Fragen adressieren. Der Ansatz des Unternehmens, Gemini 1.5 Pro schrittweise
einzuführen, deutet auf eine Strategie hin, die Risiken mindern soll, bevor sie zu erheblichen
Problemen werden &lt;small&gt;&lt;a href=&quot;#ref7&quot;&gt;[7]&lt;/a&gt;&lt;/small&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Marktchancen ausweiten&lt;/strong&gt;: Google kann die Fähigkeiten von Gemini 1.5 Pro nutzen, um neue
Marktchancen zu schaffen, besonders in Sektoren, die große Datensätze und komplexe
Problemlösungsszenarien verarbeiten müssen. Indem Google Werkzeuge bereitstellt, die die
Integration von KI in Geschäftsprozesse vereinfachen, kann das Unternehmen Branchen dabei helfen,
ihre Abläufe zu transformieren und effizienter zu werden &lt;small&gt;&lt;a href=&quot;#ref10&quot;&gt;[10]&lt;/a&gt;&lt;/small&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Ethische KI-Entwicklung&lt;/strong&gt;: Je mächtiger KI-Technologien werden, desto bedeutsamer werden ihre
ethischen Implikationen. Googles fortgesetztes Bekenntnis zu verantwortungsvoller KI-Entwicklung,
wie es sich in den KI-Prinzipien und Governance-Rahmenwerken des Unternehmens zeigt, wird
entscheidend sein, um öffentliches Vertrauen und regulatorische Konformität zu erhalten
&lt;small&gt;&lt;a href=&quot;#ref11&quot;&gt;[11]&lt;/a&gt;&lt;/small&gt;.&lt;/p&gt;
&lt;p&gt;Zusammenfassend lässt sich sagen: Der Rollout von Gemini 1.5 Pro bringt zwar Herausforderungen
mit sich, eröffnet Google aber auch erhebliche Chancen, im KI-Bereich eine führende Rolle
einzunehmen. Wenn das Unternehmen weiterhin auf Leistungsverbesserungen, ethische KI-Entwicklung
und Marktausweitung setzt, kann es diese Herausforderungen wirksam bewältigen und neue Standards
dafür setzen, was KI in verschiedenen Branchen leisten kann.&lt;/p&gt;
&lt;h3&gt;Fazit&lt;/h3&gt;
&lt;p&gt;Googles KI-Reise veranschaulicht die komplexe Dynamik von technologischer Innovation,
Markterwartungen und Unternehmensstrategie. Obwohl das Unternehmen sowohl bemerkenswerte Erfolge
als auch Rückschläge erlebt hat, ziehen seine laufenden KI-Bemühungen weiterhin erhebliche
Aufmerksamkeit am Markt auf sich. Investoren und Marktbeobachter dürften genau verfolgen, ob
Google seine KI-Fähigkeiten in nachhaltiges Wachstum und Marktführerschaft übersetzen kann.&lt;/p&gt;
&lt;p&gt;Da sich die KI-Landschaft weiter rasant entwickelt, wird Googles Stellung als KI-Pionier auf die
Probe gestellt. Die Fähigkeit des Unternehmens, ehrgeizige Innovation mit realistischer Umsetzung,
transparenter Kommunikation und wirksamem Erwartungsmanagement auszubalancieren, wird ein
entscheidender Faktor für den langfristigen Erfolg im KI-Bereich und für die gesamte
Marktentwicklung sein.&lt;/p&gt;
&lt;h3&gt;Referenzen&lt;/h3&gt;
&lt;p&gt;&lt;a id=&quot;ref1&quot;&gt;&lt;/a&gt;1. &lt;a href=&quot;https://emeritus.org/blog/ai-strategy-google/&quot;&gt;Emeritus - Google AI Strategy&lt;/a&gt;&lt;br&gt;
&lt;a id=&quot;ref2&quot;&gt;&lt;/a&gt;2. &lt;a href=&quot;https://blog.google/products/google-one/google-one-gemini-ai-gmail-docs-sheets/&quot;&gt;Google Blog - Gemini AI&lt;/a&gt;&lt;br&gt;
&lt;a id=&quot;ref3&quot;&gt;&lt;/a&gt;3. &lt;a href=&quot;https://finance.yahoo.com/quote/GOOGL/&quot;&gt;Yahoo Finance - GOOGL Quote&lt;/a&gt;&lt;br&gt;
&lt;a id=&quot;ref4&quot;&gt;&lt;/a&gt;4. &lt;a href=&quot;https://en.wikipedia.org/wiki/Closing_milestones_of_the_S%26P_500&quot;&gt;Wikipedia - S&amp;#x26;P 500 Milestones&lt;/a&gt;&lt;br&gt;
&lt;a id=&quot;ref5&quot;&gt;&lt;/a&gt;5. &lt;a href=&quot;https://www.thinkwithgoogle.com/intl/en-emea/marketing-strategies/automation/using-google-ai-tools/&quot;&gt;Think with Google - AI Tools&lt;/a&gt;&lt;br&gt;
&lt;a id=&quot;ref6&quot;&gt;&lt;/a&gt;6. &lt;a href=&quot;https://timesofindia.indiatimes.com/gadgets-news/google-releases-gemini-15-pro-ai-model-heres-what-company-ceo-sundar-pichai-has-to-say/articleshow/107732867.cms&quot;&gt;Times of India - Gemini 15 Pro AI Release&lt;/a&gt;&lt;br&gt;
&lt;a id=&quot;ref7&quot;&gt;&lt;/a&gt;7. &lt;a href=&quot;https://encord.com/blog/google-gemini-1-5-generative-ai-model-with-mixture-of-experts/&quot;&gt;Encord - Google Gemini 1.5 Generative AI Model with Mixture of Experts&lt;/a&gt;&lt;br&gt;
&lt;a id=&quot;ref8&quot;&gt;&lt;/a&gt;8. &lt;a href=&quot;https://builtin.com/articles/google-gemini&quot;&gt;Built In - Google Gemini&lt;/a&gt;&lt;br&gt;
&lt;a id=&quot;ref9&quot;&gt;&lt;/a&gt;9. &lt;a href=&quot;https://www.cbsnews.com/news/google-artificial-intelligence-future-60-minutes-transcript-2023-04-16/&quot;&gt;CBS News - Google&apos;s AI Future&lt;/a&gt;&lt;br&gt;
&lt;a id=&quot;ref10&quot;&gt;&lt;/a&gt;10. &lt;a href=&quot;https://cloud.google.com/ai/generative-ai&quot;&gt;Google Cloud - Generative AI&lt;/a&gt;&lt;br&gt;
&lt;a id=&quot;ref11&quot;&gt;&lt;/a&gt;11. &lt;a href=&quot;https://blog.google/technology/ai/responsible-ai-looking-back-at-2022-and-to-the-future/&quot;&gt;Google Blog - Responsible AI&lt;/a&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Stable-Diffusion-Fotorealismus: Leitfaden zu Einstellungen und GPU-Grenzen]]></title><description><![CDATA[Aktualisiert im März 2026. Dieser Artikel wurde ursprünglich im Mai 2023 geschrieben, als SD 1.5 mit 512x512 der Standard war und die RTX…]]></description><link>https://bdteo.com/de/pushing-the-stable-diffussion-limits/</link><guid isPermaLink="false">https://bdteo.com/de/pushing-the-stable-diffussion-limits/</guid><pubDate>Thu, 04 May 2023 23:45:00 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Aktualisiert im März 2026.&lt;/strong&gt; Dieser Artikel wurde ursprünglich im Mai 2023 geschrieben, als SD 1.5 mit 512x512 der Standard war und die RTX 3090 die Spitzenhardware. Alles hat sich verändert. Flux 2, SDXL-Fine-Tunes, SD 3.5, ControlNet und die RTX 5090 haben völlig neu definiert, was möglich ist. Das hier ist der aktuelle Stand.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Die Lücke zwischen KI-generierten Bildern und echten Fotografien ist fast geschlossen. 2023 bedeutete &quot;fotorealistisch&quot;: &quot;fast überzeugend, wenn man die Augen zusammenkneift.&quot; 2026 erzeugen die besten Modelle Bilder, die tatsächlich schwer von professioneller Fotografie zu unterscheiden sind.&lt;/p&gt;
&lt;p&gt;So kommt man dorthin.&lt;/p&gt;
&lt;h2&gt;Die aktuelle Landschaft des Fotorealismus&lt;/h2&gt;
&lt;p&gt;Das Modell, das du auswählst, ist wichtiger als jede Einstellung, an der du drehst. So sieht der Stand aus:&lt;/p&gt;
&lt;h3&gt;Flux 2 -- Der neue König&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://bfl.ai/models/flux-2&quot;&gt;Flux 2&lt;/a&gt; von Black Forest Labs (veröffentlicht im November 2025) ist 2026 wohl das beste Open-Weight-Modell für Fotorealismus &lt;small&gt;&lt;a href=&quot;#ref1&quot;&gt;[1]&lt;/a&gt;&lt;/small&gt;. Es erzeugt Bilder mit natürlichem Licht, präzisen Hauttexturen und kohärenter Komposition, die mit professioneller Fotografie mithalten kann. Adobe hat Flux (Kontext Pro) im September 2025 in Photoshop integriert &lt;small&gt;&lt;a href=&quot;#ref2&quot;&gt;[2]&lt;/a&gt;&lt;/small&gt; -- das sagt einiges darüber, wo das Vertrauen der Branche liegt.&lt;/p&gt;
&lt;p&gt;Flux versteht außerdem natürliche Sprache außergewöhnlich gut. Du kannst in schlichtem Englisch beschreiben, was du willst, ohne die Keyword-Suppe, die SD 1.5 verlangt hat.&lt;/p&gt;
&lt;h3&gt;SDXL-Fine-Tunes -- Die Arbeitstiere&lt;/h3&gt;
&lt;p&gt;Für SDXL-basierten Fotorealismus sind das die aktuellen Spitzenreiter:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Juggernaut XL v9/v10&lt;/strong&gt; -- die Standardwahl für cineastische, fotografische Ergebnisse. Besonders beliebt bei Fotografen und Filmemachern.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Realistic Vision&lt;/strong&gt; -- speziell für lebensechte Texturen, Beleuchtung und Gesichtsgenauigkeit fine-getuned.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;EpicRealism&lt;/strong&gt; -- außergewöhnlich starke feine Details und natürliches Licht.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Diese Modelle haben enorme Community-Unterstützung, umfangreiche LoRA-Bibliotheken und vorhersehbares Verhalten. Wenn Flux sich noch zu neu anfühlt oder dein Workflow auf SDXL aufgebaut ist, sind sie ausgezeichnete Optionen.&lt;/p&gt;
&lt;h3&gt;SD 3.5 Large&lt;/h3&gt;
&lt;p&gt;Das Flaggschiff von Stability AI nutzt die neue Multimodal Diffusion Transformer (MMDiT)-Architektur -- ein grundlegend anderer Ansatz als SDXL. Es ist technisch beeindruckend, aber das Ökosystem ist kleiner. SD 3.0 wurde im April 2025 eingestellt, also stell sicher, dass du auf 3.5 bist &lt;small&gt;&lt;a href=&quot;#ref3&quot;&gt;[3]&lt;/a&gt;&lt;/small&gt;.&lt;/p&gt;
&lt;h2&gt;GPU-Realitätscheck&lt;/h2&gt;
&lt;p&gt;Die Hardware-Anforderungen sind deutlich gestiegen.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;GPU&lt;/th&gt;
&lt;th&gt;VRAM&lt;/th&gt;
&lt;th&gt;Fotorealismus-Fähigkeit&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;RTX 3060 12GB&lt;/td&gt;
&lt;td&gt;12GB&lt;/td&gt;
&lt;td&gt;Nur SD 1.5-Fotorealismus. SDXL ist knapp&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;RTX 4070 Ti&lt;/td&gt;
&lt;td&gt;12GB&lt;/td&gt;
&lt;td&gt;SDXL bei 1024x1024. Flux ist mit Optimierungen möglich&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;RTX 4090&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;24GB&lt;/td&gt;
&lt;td&gt;Der Sweet Spot. Bewältigt SDXL, Flux und SD 3.5 komfortabel bei 1024x1024+&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;RTX 5090&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;32GB&lt;/td&gt;
&lt;td&gt;Alles, inklusive 4K-Generierung und Batch-Workflows. 32GB GDDR7, 512-bit Bus &lt;small&gt;&lt;a href=&quot;#ref4&quot;&gt;[4]&lt;/a&gt;&lt;/small&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;8GB-Karten&lt;/td&gt;
&lt;td&gt;8GB&lt;/td&gt;
&lt;td&gt;Mit ComfyUIs VRAM-Management minimal brauchbar. Nicht angenehm&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Der Sweet Spot von 2023, &quot;512x512 auf einer RTX 3080&quot;, ist alte Geschichte. &lt;strong&gt;1024x1024 ist jetzt die Standardauflösung&lt;/strong&gt;, und du willst mindestens 16GB VRAM, um ohne ständige Frustration zu arbeiten. Bei 24GB wird es komfortabel.&lt;/p&gt;
&lt;p&gt;Speziell für Fotorealismus bedeutet mehr VRAM, dass du größere Modelle, höhere Auflösungen und ControlNet gleichzeitig ausführen kannst, ohne auf die CPU auszulagern.&lt;/p&gt;
&lt;h2&gt;Einstellungen für Fotorealismus&lt;/h2&gt;
&lt;h3&gt;Sampler&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;DPM++ 2M Karras&lt;/strong&gt; mit 25-30 Steps. Das ist der gefestigte Konsens für SDXL-Fotorealismus -- das beste Verhältnis von Geschwindigkeit zu Qualität. Wenn du bei niedrigen Step-Zahlen etwas mehr Detail möchtest, wechsle zu &lt;strong&gt;DPM++ SDE Karras&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Für Flux: nutze den Standard-Sampler mit 20-30 Steps.&lt;/p&gt;
&lt;h3&gt;CFG&lt;/h3&gt;
&lt;p&gt;Für SDXL-Fotorealismus: &lt;strong&gt;7-9&lt;/strong&gt;. Das gibt starke Prompt-Treue ohne den übersättigten, überkochten Look, der oberhalb von 10 entsteht.&lt;/p&gt;
&lt;p&gt;Für SD 3.5: niedriger anfangen (&lt;strong&gt;3-5&lt;/strong&gt;) -- der Guidance-Mechanismus funktioniert anders.&lt;/p&gt;
&lt;p&gt;Für Flux: modell-spezifischen Empfehlungen folgen, aber im Allgemeinen niedriger als bei SDXL.&lt;/p&gt;
&lt;h3&gt;Auflösung&lt;/h3&gt;
&lt;p&gt;Generiere in der nativen Auflösung des Modells (1024x1024 für SDXL/SD 3.5/Flux), dann &lt;strong&gt;upscale&lt;/strong&gt; für höhere Auflösung. Versuch nicht, direkt bei 2048x2048 zu generieren -- du bekommst Artefakte, duplizierte Elemente und Kompositionsprobleme.&lt;/p&gt;
&lt;p&gt;Upscaling-Optionen: Hi-res fix in A1111 oder dedizierte Upscaling-Nodes in ComfyUI (4x-UltraSharp, ESRGAN).&lt;/p&gt;
&lt;h3&gt;Prompting für Fotorealismus&lt;/h3&gt;
&lt;p&gt;Die größte Verschiebung seit 2023: &lt;strong&gt;natürlich schreiben, nicht in Keywords&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;SD 1.5 brauchte Prompts wie:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;portrait of a woman, photorealistic, 8k, ultra detailed, sharp focus,
professional photography, Fujifilm X-T4, 85mm f/1.4&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;SDXL und Flux verstehen:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;A portrait of a woman in soft afternoon light, photographed with a shallow
depth of field. She&apos;s looking slightly off-camera with a natural expression.&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Der Keyword-Suppe-Ansatz funktioniert bei SDXL weiterhin, aber natürliche Sprache erzeugt kohärentere Ergebnisse. Besonders Flux glänzt bei beschreibenden, konversationellen Prompts.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Negative Prompts:&lt;/strong&gt; Halte sie minimal. Fang ohne an und füge dann gezielte Korrekturen hinzu. &quot;cartoon, illustration, painting&quot; reicht meistens, um die Dinge fotorealistisch zu halten. Sieh dir das &lt;a href=&quot;/stable-difussion-cheat-sheet/&quot;&gt;Cheat Sheet&lt;/a&gt; für den vollständigen Philosophiewechsel bei Negative Prompts an.&lt;/p&gt;
&lt;h2&gt;ControlNet verändert alles&lt;/h2&gt;
&lt;p&gt;Wenn du es mit fotorealistischer Komposition ernst meinst, ist ControlNet nicht verhandelbar. Es lässt dich die Struktur deines Bildes steuern über:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Depth maps&lt;/strong&gt; -- räumliche Beziehungen und Perspektive erhalten&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Canny edge detection&lt;/strong&gt; -- Umrisse und Formen bewahren&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;OpenPose&lt;/strong&gt; -- menschliche Posen und Körperproportionen steuern&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Surface normals&lt;/strong&gt; -- realistische Lichtinteraktion mit Oberflächen&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;ControlNet-Modelle sind inzwischen für SDXL, Flux und SD 3.5 verfügbar &lt;small&gt;&lt;a href=&quot;#ref5&quot;&gt;[5]&lt;/a&gt;&lt;/small&gt;. Multi-ControlNet (das Stapeln mehrerer Controls) gibt dir präzise Kompositionskontrolle, die Prompt Engineering allein nicht erreichen kann.&lt;/p&gt;
&lt;p&gt;Der Workflow: Nimm ein Referenzfoto, extrahiere eine Depth Map oder Pose, nutze sie als ControlNet-Input und generiere ein fotorealistisches Bild mit derselben Komposition.&lt;/p&gt;
&lt;h2&gt;Geschwindigkeit vs. Qualität&lt;/h2&gt;
&lt;p&gt;Wenn du schnelle Iterationen brauchst (Konzeptarbeit, Prompt-Tests), nutze &lt;strong&gt;SDXL Lightning&lt;/strong&gt; -- es erzeugt hochwertige 1024px-Bilder in 2-8 Steps &lt;small&gt;&lt;a href=&quot;#ref6&quot;&gt;[6]&lt;/a&gt;&lt;/small&gt;. Bei höheren Auflösungen ist die Qualität besser als bei SDXL Turbo.&lt;/p&gt;
&lt;p&gt;Für das finale Ergebnis wechselst du zurück zu vollem SDXL oder Flux mit 25-30 Steps. Der Unterschied ist sichtbar.&lt;/p&gt;
&lt;h2&gt;Der praktische Workflow&lt;/h2&gt;
&lt;p&gt;Das funktioniert 2026 tatsächlich für fotorealistische Ergebnisse:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Wähle dein Modell&lt;/strong&gt; -- Flux 2 für den besten Fotorealismus, Juggernaut XL für das SDXL-Ökosystem&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Schreibe einen natürlichsprachlichen Prompt&lt;/strong&gt;, der beschreibt, was du siehst&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Generiere bei 1024x1024&lt;/strong&gt;, DPM++ 2M Karras, CFG 7-9, 25-30 Steps&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Nutze ControlNet&lt;/strong&gt;, wenn du eine bestimmte Komposition brauchst (Depth oder Pose)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Iteriere am Prompt&lt;/strong&gt; -- generiere 4-8 Bilder, wähle das beste&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Upscale&lt;/strong&gt; den Gewinner auf deine Zielauflösung&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Inpainte&lt;/strong&gt; problematische Bereiche (Hände, Augen, kleine Details)&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Das ist derselbe Workflow, egal ob du in ComfyUI oder A1111 bist. Die Werkzeuge unterscheiden sich, die Pipeline nicht.&lt;/p&gt;
&lt;hr&gt;
&lt;h3&gt;Referenzen&lt;/h3&gt;
&lt;p&gt;&lt;a id=&quot;ref1&quot;&gt;&lt;/a&gt;1. &lt;a href=&quot;https://bfl.ai/models/flux-2&quot;&gt;Flux 2 Models -- Black Forest Labs&lt;/a&gt; -- &lt;em&gt;Offizielle Flux 2-Modellseite.&lt;/em&gt;&lt;br&gt;
&lt;a id=&quot;ref2&quot;&gt;&lt;/a&gt;2. &lt;a href=&quot;https://blogs.nvidia.com/blog/rtx-ai-garage-flux-2-comfyui/&quot;&gt;FLUX.2 and NVIDIA RTX AI Garage&lt;/a&gt; -- &lt;em&gt;Flux 2-Integration mit ComfyUI und Branchenadoption.&lt;/em&gt;&lt;br&gt;
&lt;a id=&quot;ref3&quot;&gt;&lt;/a&gt;3. &lt;a href=&quot;https://platform.stability.ai/docs/release-notes&quot;&gt;Stability AI Release Notes&lt;/a&gt; -- &lt;em&gt;Details zur Einstellung von SD 3.0 und zur Veröffentlichung von 3.5.&lt;/em&gt;&lt;br&gt;
&lt;a id=&quot;ref4&quot;&gt;&lt;/a&gt;4. &lt;a href=&quot;https://www.bestgpusforai.com/gpu-comparison/5090-vs-4090&quot;&gt;RTX 5090 vs 4090 for AI Workloads&lt;/a&gt; -- &lt;em&gt;Hardwarevergleich für Bildgenerierung.&lt;/em&gt;&lt;br&gt;
&lt;a id=&quot;ref5&quot;&gt;&lt;/a&gt;5. &lt;a href=&quot;https://stable-diffusion-art.com/controlnet/&quot;&gt;ControlNet Complete Guide&lt;/a&gt; -- &lt;em&gt;Aktualisierte ControlNet-Dokumentation für mehrere Architekturen.&lt;/em&gt;&lt;br&gt;
&lt;a id=&quot;ref6&quot;&gt;&lt;/a&gt;6. &lt;a href=&quot;https://huggingface.co/ByteDance/SDXL-Lightning&quot;&gt;SDXL-Lightning by ByteDance&lt;/a&gt; -- &lt;em&gt;Generierungsmodell mit 2-8 Steps.&lt;/em&gt;&lt;br&gt;
&lt;a id=&quot;ref7&quot;&gt;&lt;/a&gt;7. &lt;a href=&quot;https://www.cubix.co/blog/best-model-for-stable-diffusion/&quot;&gt;Best Stable Diffusion Models for Photorealism 2026&lt;/a&gt; -- &lt;em&gt;Aktuelle Modelllandschaft.&lt;/em&gt;&lt;br&gt;
&lt;a id=&quot;ref8&quot;&gt;&lt;/a&gt;8. &lt;a href=&quot;https://civitai.com/articles/2115/top-5-photorealistic-stable-diffusion-models-reviewed&quot;&gt;Top Photorealistic Stable Diffusion Models&lt;/a&gt; -- &lt;em&gt;Community-Reviews von Civitai.&lt;/em&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h3&gt;Verwandte Beiträge&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;/stable-difussion-cheat-sheet/&quot;&gt;Stable Diffusion Cheat Sheet: Troubleshooting &amp;#x26; Optimization&lt;/a&gt; -- Schnellreferenz für Parameter, Sampler und Troubleshooting.&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[Stable-Diffusion-Spickzettel: Fehlersuche & Optimierung]]></title><description><![CDATA[Aktualisiert im März 2026. Die ursprüngliche Version dieses Spickzettels wurde im Mai 2023 für SD 1.5 geschrieben. Seitdem hat sich fast…]]></description><link>https://bdteo.com/de/stable-difussion-cheat-sheet/</link><guid isPermaLink="false">https://bdteo.com/de/stable-difussion-cheat-sheet/</guid><pubDate>Thu, 04 May 2023 23:30:00 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Aktualisiert im März 2026.&lt;/strong&gt; Die ursprüngliche Version dieses Spickzettels wurde im Mai 2023 für SD 1.5 geschrieben. Seitdem hat sich fast alles verändert -- neue Architekturen (SDXL, SD 3.5, Flux), neue UIs (ComfyUI), neue Hardware (RTX 5090) und eine komplette Kehrtwende bei der Philosophie zu Negative Prompts. Das hier ist die aktuelle Version.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Das ist meine Arbeitsreferenz für Stable-Diffusion-Parameter. Kein Tutorial -- nur die Einstellungen, zu denen ich greife, wenn etwas nicht funktioniert oder wenn ich die Qualität weiter treiben will.&lt;/p&gt;
&lt;h2&gt;Welches Modell du verwenden solltest&lt;/h2&gt;
&lt;p&gt;Das ist inzwischen die erste Entscheidung, und sie ist wichtiger als jede Parameter-Feinjustierung.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Modell&lt;/th&gt;
&lt;th&gt;Am besten für&lt;/th&gt;
&lt;th&gt;Auflösung&lt;/th&gt;
&lt;th&gt;Hinweise&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Flux 2&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Fotorealismus, Prompt-Treue&lt;/td&gt;
&lt;td&gt;1024x1024+&lt;/td&gt;
&lt;td&gt;Bestes Open-Weight-Modell für Fotorealismus im Jahr 2026. In Adobe Photoshop integriert &lt;small&gt;&lt;a href=&quot;#ref1&quot;&gt;[1]&lt;/a&gt;&lt;/small&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;SDXL&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Allgemeine Nutzung&lt;/td&gt;
&lt;td&gt;1024x1024&lt;/td&gt;
&lt;td&gt;Riesiges Ökosystem an Fine-Tunes. Juggernaut XL, Realistic Vision, DreamShaper&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;SD 3.5 Large&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Höchste Qualität (Stabilitys Flaggschiff)&lt;/td&gt;
&lt;td&gt;1024x1024&lt;/td&gt;
&lt;td&gt;MMDiT-Architektur. SD 3.0 wurde im April 2025 eingestellt &lt;small&gt;&lt;a href=&quot;#ref2&quot;&gt;[2]&lt;/a&gt;&lt;/small&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;SDXL Lightning&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Geschwindigkeit&lt;/td&gt;
&lt;td&gt;1024x1024&lt;/td&gt;
&lt;td&gt;Generierung in 2-8 Schritten. Bessere Qualität als Turbo bei höherer Auflösung &lt;small&gt;&lt;a href=&quot;#ref3&quot;&gt;[3]&lt;/a&gt;&lt;/small&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;SD 1.5&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Legacy-Workflows&lt;/td&gt;
&lt;td&gt;512x512&lt;/td&gt;
&lt;td&gt;Riesige Fine-Tune-Bibliothek, wird aber zunehmend abgelöst. SD 2.0/2.1 offiziell eingestellt&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Wenn du frisch anfängst: &lt;strong&gt;Flux 2 für Fotorealismus, SDXL für alles andere.&lt;/strong&gt; SD 3.5 ist gut, aber das Ökosystem ist kleiner.&lt;/p&gt;
&lt;h2&gt;Welche UI du verwenden solltest&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;UI&lt;/th&gt;
&lt;th&gt;Am besten für&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;ComfyUI&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Power-User. Node-basiert, besseres VRAM-Management, 15% schneller, beste Flux-Unterstützung. Seit 2025 der Branchenstandard für ernsthafte Arbeit &lt;small&gt;&lt;a href=&quot;#ref4&quot;&gt;[4]&lt;/a&gt;&lt;/small&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Automatic1111&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Einsteiger. Einfachere Oberfläche, riesige Erweiterungsbibliothek. Funktioniert für SDXL weiterhin ordentlich&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Fooocus&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Ein-Klick-Generierung. Minimale Konfiguration. Gut für schnelle Ergebnisse&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Ich nutze ComfyUI. Die Lernkurve ist steiler (rechne mit 10-20 Stunden, bis es sich vertraut anfühlt), aber allein das VRAM-Management ist es wert -- es lässt SDXL auf 8 GB laufen, wo A1111 abstürzt.&lt;/p&gt;
&lt;h2&gt;Sampler&lt;/h2&gt;
&lt;p&gt;Die Sampler-Debatte ist weitgehend entschieden.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Meine Standardwahl:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;DPM++ 2M Karras&lt;/strong&gt; -- bestes Verhältnis aus Geschwindigkeit und Qualität. Das ist mein Default für fast alles.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;DPM++ SDE Karras&lt;/strong&gt; -- bei niedrigen Schrittzahlen etwas besser. Gut, wenn du schnell iterierst.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Euler a&lt;/strong&gt; -- weiterhin zuverlässig. Mehr Varianz in den Ausgaben, gut zum Erkunden.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Wann du wechseln solltest:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Zu wenig Vielfalt in den Ausgaben? Probier DPM++ SDE oder Euler a.&lt;/li&gt;
&lt;li&gt;Artefakte oder Übersättigung? Probier DPM++ 2M Karras oder plain Euler.&lt;/li&gt;
&lt;li&gt;Geschwindigkeit über alles? Euler a oder DPM++ 2M (non-Karras).&lt;/li&gt;
&lt;li&gt;Maximale Qualität? DPM++ 3M SDE Karras oder UniPC.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Schrittzahlen:&lt;/strong&gt; 20-30 Schritte für die meisten Sampler. Lightning-Modelle brauchen nur 2-8.&lt;/p&gt;
&lt;h2&gt;CFG (Classifier Free Guidance)&lt;/h2&gt;
&lt;p&gt;Wie strikt das Modell deinem Prompt folgt statt seiner eigenen Interpretation.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Bereich&lt;/th&gt;
&lt;th&gt;Wirkung&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1-4&lt;/td&gt;
&lt;td&gt;Sehr kreativ, lockere Interpretation. Oft inkohärent&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;5-7&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Gute Balance für die meisten Arbeiten&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;7-10&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Starke Prompt-Treue. Sweet Spot für SDXL-Fotorealismus&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;10-15&lt;/td&gt;
&lt;td&gt;Risiko für Artefakte und überkochte Farben&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;15+&lt;/td&gt;
&lt;td&gt;Fast immer zu viel. Artefakte garantiert&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;Hinweis:&lt;/strong&gt; SD 3.5 nutzt einen anderen Guidance-Mechanismus. Das CFG-Konzept gilt weiterhin, aber die Skala verhält sich anders -- fang niedriger an (3-5) und justiere dann nach.&lt;/p&gt;
&lt;h2&gt;Auflösung&lt;/h2&gt;
&lt;p&gt;Die Zeiten von 512x512 sind vorbei.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Modell&lt;/th&gt;
&lt;th&gt;Native Auflösung&lt;/th&gt;
&lt;th&gt;Praktischer Bereich&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;SD 1.5&lt;/td&gt;
&lt;td&gt;512x512&lt;/td&gt;
&lt;td&gt;512x512 bis 768x768&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;SDXL&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;1024x1024&lt;/td&gt;
&lt;td&gt;1024x1024 (Standard), 1024x768, 768x1024&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;SD 3.5&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;1024x1024&lt;/td&gt;
&lt;td&gt;1024x1024+&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Flux&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;1024x1024&lt;/td&gt;
&lt;td&gt;1024x1024+, 4K auf High-End-GPUs möglich&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Über die native Auflösung hinauszugehen riskiert Artefakte und Kompositionsprobleme. Nutze Hi-Res Fix oder Upscaling, statt direkt mit 2048x2048 zu generieren.&lt;/p&gt;
&lt;h2&gt;Clip Skip&lt;/h2&gt;
&lt;p&gt;Weniger relevant als früher.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;SD 1.5:&lt;/strong&gt; Clip skip 1-2 ist sehr wichtig. Anime-Modelle nutzen oft clip skip 2.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;SDXL:&lt;/strong&gt; Nutzt zwei Text-Encoder (CLIP + OpenCLIP). Clip skip wird meistens ignoriert -- die Architektur geht anders damit um.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;SD 3.5 / Flux:&lt;/strong&gt; Nicht auf dieselbe Weise anwendbar. Diese Modelle nutzen transformerbasiertes Text-Encoding.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Wenn du SDXL oder neuer nutzt: Mach dir um clip skip keine Sorgen. Wenn du SD 1.5 nutzt: Lass es für Fotorealismus bei 1, für Anime bei 2.&lt;/p&gt;
&lt;h2&gt;Negative Prompts&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Die Philosophie hat sich umgedreht.&lt;/strong&gt; 2023 lautete der Rat, lange Negative-Prompt-Listen zu verwenden. 2026 ist der Konsens: &lt;strong&gt;fang mit nichts an und füge nur hinzu, was du wirklich korrigieren musst.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Warum die Änderung:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;SDXL und Flux verstehen natürliche Sprache deutlich besser als SD 1.5&lt;/li&gt;
&lt;li&gt;Lange Negative Prompts können Kreativität tatsächlich &lt;em&gt;einschränken&lt;/em&gt; und schlechtere Ergebnisse erzeugen&lt;/li&gt;
&lt;li&gt;&quot;bad anatomy&quot; ist zu vage, um nützlich zu sein. &quot;ugly&quot; funktioniert nicht, weil SD nicht mit als &quot;ugly&quot; gelabelten Bildern trainiert wurde&lt;/li&gt;
&lt;li&gt;Manche Modelle schneiden mit langen Negatives nachweislich schlechter ab &lt;small&gt;&lt;a href=&quot;#ref5&quot;&gt;[5]&lt;/a&gt;&lt;/small&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Aktueller Ansatz:&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Generiere zuerst ohne jeden Negative Prompt.&lt;/li&gt;
&lt;li&gt;Wenn du ein konkretes Problem siehst (zusätzliche Finger, unscharfer Hintergrund), füge dafür ein gezieltes Negative hinzu.&lt;/li&gt;
&lt;li&gt;Nutze Emphasis Weighting: &lt;code class=&quot;language-text&quot;&gt;(blurry:1.3)&lt;/code&gt; statt nur &lt;code class=&quot;language-text&quot;&gt;blurry&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Halt es kurz -- maximal 5-10 Begriffe.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;GPU-Kurzreferenz&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;GPU&lt;/th&gt;
&lt;th&gt;VRAM&lt;/th&gt;
&lt;th&gt;Gut für&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;RTX 3060 12GB&lt;/td&gt;
&lt;td&gt;12GB&lt;/td&gt;
&lt;td&gt;SD 1.5, einfaches SDXL&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;RTX 4070 Ti&lt;/td&gt;
&lt;td&gt;12GB&lt;/td&gt;
&lt;td&gt;SDXL, etwas Flux&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;RTX 4090&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;24GB&lt;/td&gt;
&lt;td&gt;Alles. Das Arbeitspferd&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;RTX 5090&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;32GB&lt;/td&gt;
&lt;td&gt;Alles, einschließlich 4K und Batch-Generierung&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;8GB-Karten&lt;/td&gt;
&lt;td&gt;8GB&lt;/td&gt;
&lt;td&gt;Minimal brauchbar. ComfyUI hilft beim VRAM-Management&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Ab 24 GB wird es für SDXL und Flux komfortabel, ohne ständig mit VRAM jonglieren zu müssen.&lt;/p&gt;
&lt;h2&gt;Schnelle Troubleshooting-Fixes&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Problem&lt;/th&gt;
&lt;th&gt;Versuch&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Unscharfe Ausgabe&lt;/td&gt;
&lt;td&gt;Schritte erhöhen. Prüfen, ob die Auflösung zur nativen Auflösung des Modells passt&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Zusätzliche Finger/Gliedmaßen&lt;/td&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;extra fingers, extra limbs&lt;/code&gt; zum Negative Prompt hinzufügen. Oder ControlNet verwenden&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Übersättigte Farben&lt;/td&gt;
&lt;td&gt;CFG senken. Zu DPM++ 2M Karras wechseln&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Komposition ist falsch&lt;/td&gt;
&lt;td&gt;ControlNet (depth, canny, pose) nutzen, statt gegen den Prompt zu kämpfen&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Generierung ist langsam&lt;/td&gt;
&lt;td&gt;Lightning-Modell nutzen, Schritte reduzieren, ComfyUI für besseres VRAM verwenden&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Kein VRAM mehr&lt;/td&gt;
&lt;td&gt;Zu ComfyUI wechseln, Batch-Größe reduzieren, fp16 verwenden&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr&gt;
&lt;h3&gt;Referenzen&lt;/h3&gt;
&lt;p&gt;&lt;a id=&quot;ref1&quot;&gt;&lt;/a&gt;1. &lt;a href=&quot;https://blogs.nvidia.com/blog/rtx-ai-garage-flux-2-comfyui/&quot;&gt;Flux 2 and NVIDIA RTX AI Integration&lt;/a&gt; -- &lt;em&gt;NVIDIAs Berichterstattung zu Flux 2 mit ComfyUI.&lt;/em&gt;&lt;br&gt;
&lt;a id=&quot;ref2&quot;&gt;&lt;/a&gt;2. &lt;a href=&quot;https://platform.stability.ai/docs/release-notes&quot;&gt;Stability AI Release Notes&lt;/a&gt; -- &lt;em&gt;SD-3.0-Einstellung und 3.5-Release.&lt;/em&gt;&lt;br&gt;
&lt;a id=&quot;ref3&quot;&gt;&lt;/a&gt;3. &lt;a href=&quot;https://huggingface.co/ByteDance/SDXL-Lightning&quot;&gt;SDXL-Lightning by ByteDance&lt;/a&gt; -- &lt;em&gt;Generierung in 2-8 Schritten bei 1024 px.&lt;/em&gt;&lt;br&gt;
&lt;a id=&quot;ref4&quot;&gt;&lt;/a&gt;4. &lt;a href=&quot;https://wiki.shakker.ai/en/comfyui-vs-automatic1111&quot;&gt;ComfyUI vs Automatic1111 2026 Comparison&lt;/a&gt; -- &lt;em&gt;Performance- und Feature-Vergleich.&lt;/em&gt;&lt;br&gt;
&lt;a id=&quot;ref5&quot;&gt;&lt;/a&gt;5. &lt;a href=&quot;https://stable-diffusion-art.com/how-to-use-negative-prompts/&quot;&gt;How to Use Negative Prompts Effectively&lt;/a&gt; -- &lt;em&gt;Aktualisierter Leitfaden zur minimalistischen Negative-Prompt-Philosophie.&lt;/em&gt;&lt;br&gt;
&lt;a id=&quot;ref6&quot;&gt;&lt;/a&gt;6. &lt;a href=&quot;https://civitai.com/articles/7484/understanding-stable-diffusion-samplers-beyond-image-comparisons&quot;&gt;Understanding Stable Diffusion Samplers&lt;/a&gt; -- &lt;em&gt;Sampler-Vergleich und Auswahlhilfe.&lt;/em&gt;&lt;br&gt;
&lt;a id=&quot;ref7&quot;&gt;&lt;/a&gt;7. &lt;a href=&quot;https://www.cubix.co/blog/best-model-for-stable-diffusion/&quot;&gt;Best Stable Diffusion Models for 2026&lt;/a&gt; -- &lt;em&gt;Aktuelle Modelllandschaft.&lt;/em&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h3&gt;Verwandte Beiträge&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;/pushing-the-stable-diffussion-limits/&quot;&gt;Stable Diffusion Photorealism: Settings &amp;#x26; GPU Limits Guide&lt;/a&gt; -- tiefer Einstieg in fotorealistische Ergebnisse mit aktuellen Modellen.&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[Über mich]]></title><description><![CDATA[About-Seiten sind eine seltsame Sache, die man schreiben muss. Am Ende beschreibt man sich selbst wie einen Fremden, und dem misstraue ich…]]></description><link>https://bdteo.com/de/about/</link><guid isPermaLink="false">https://bdteo.com/de/about/</guid><pubDate>Thu, 04 May 2023 22:12:03 GMT</pubDate><content:encoded>&lt;p&gt;About-Seiten sind eine seltsame Sache, die man schreiben muss. Am Ende beschreibt man sich selbst wie einen Fremden, und dem misstraue ich.&lt;/p&gt;
&lt;p&gt;Also, schlicht. Ich bin Boris. Ich schreibe seit etwas mehr als vierzehn Jahren Software. Die Arbeit bezahlt die Miete, füttert die Hunde und gibt mir an den meisten Morgen ein Problem, an dem ich kauen kann - was, ehrlich gesagt, mehr ist, als die meisten Jobs leisten. Heute bin ich die Backend-Hälfte eines kleinen Produktteams, irgendwo zwischen Laravel, Docker und Kubernetes, meistens besorgt über Queues, die schneller leer werden sollten, als sie sich füllen, und über Indizes, die schneller wachsen, als mir lieb ist.&lt;/p&gt;
&lt;p&gt;Vor der Software habe ich an der Universität Mathematik studiert. Ganz aufgehört habe ich damit nie. Mathematik ist der Ort, an den ich gehe, wenn die Welt zu laut wird. Besonders Zahlentheorie - an einer Primzahl ist etwas Ehrliches, das ich an vielen anderen Orten nicht finde. Vieles von dem, was ich hier schreibe, beginnt aus dieser Richtung. Ein kleines mathematisches Jucken. Ein halb verstandener medizinischer Aufsatz. Ein französischer Satz, den ich viermal lesen musste. Dann ziehe ich daran und sehe, was sonst noch daran hängt.&lt;/p&gt;
&lt;p&gt;Der Blog ist der Ort, an dem dieses Ziehen passiert. Nichts davon ist Expertenarbeit. Ich lese Medizin, die ich eigentlich nicht lesen sollte. Ich lese bulgarische und französische Romane und tue so, als käme ich mit. Ich lerne langsam Deutsch, meistens, weil mich die Grammatik amüsiert. Manchmal schreibe ich C++ von Hand. Nicht für die Arbeit - nur weil die Disziplin eine eigene Sache ist. Die Essays entstehen aus all dem - stille Versuche, aufzuschreiben, was mir aufgefallen ist, bevor ich es vergesse.&lt;/p&gt;
&lt;p&gt;Ich lebe in Sofia. Zwei gerettete Straßenhunde - Кожухка und Еклер - sorgen dafür, dass ich mich selbst nicht zu ernst nehme. Sie sind schon früher gescheitert. Sie versuchen es weiter.&lt;/p&gt;
&lt;p&gt;Das ist das meiste. Es gibt noch ein kleines Freelancer-Schild, das ich unter dem Namen Percepticus aufrechterhalte, für Arbeit, die über Mundpropaganda kommt. Ansonsten sind die Essays hier, und ich bin zufrieden damit, über den Rest still zu sein.&lt;/p&gt;</content:encoded></item></channel></rss>