<?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[Начало - Boris D. Teoharov (Български)]]></title><description><![CDATA[Тихи есета за инженерство, език и онова, което се появява на ръба на всяко честно питане. Писани бавно, от София.]]></description><link>https://bdteo.com/bg/</link><generator>GatsbyJS</generator><lastBuildDate>Sun, 17 May 2026 10:53:12 GMT</lastBuildDate><atom:link href="https://bdteo.com/bg/rss.xml" rel="self" type="application/rss+xml"/><item><title><![CDATA[Слонова кост под кехлибар]]></title><description><![CDATA[Някои неща не искат да бъдат решени. Те чакат тихо и първото прочитане може да бъде достатъчно.]]></description><link>https://bdteo.com/bg/ivory-under-amber/</link><guid isPermaLink="false">https://bdteo.com/bg/ivory-under-amber/</guid><pubDate>Wed, 13 May 2026 20:15:00 GMT</pubDate><content:encoded>&lt;p&gt;Някои неща не искат да бъдат решени. Те чакат тихо и първото прочитане може да бъде достатъчно.&lt;/p&gt;
&lt;figure class=&quot;acrostic-poem&quot; aria-label=&quot;Стихотворение&quot;&gt;
  &lt;div class=&quot;acrostic-poem__stanza&quot;&gt;
    &lt;span class=&quot;acrostic-poem__line&quot;&gt;А освети ме&lt;/span&gt;
    &lt;span class=&quot;acrostic-poem__line&quot;&gt;Забуди ме бавно&lt;/span&gt;
  &lt;/div&gt;
  &lt;div class=&quot;acrostic-poem__stanza&quot;&gt;
    &lt;span class=&quot;acrostic-poem__line&quot;&gt;И гневът не ме докосва&lt;/span&gt;
    &lt;span class=&quot;acrostic-poem__line&quot;&gt;С благородство в кръвта&lt;/span&gt;
    &lt;span class=&quot;acrostic-poem__line&quot;&gt;Като заседнало време&lt;/span&gt;
    &lt;span class=&quot;acrostic-poem__line&quot;&gt;Аз крещя към каменна стена&lt;/span&gt;
    &lt;span class=&quot;acrostic-poem__line&quot;&gt;Минавам сляпо покрай истината&lt;/span&gt;
  &lt;/div&gt;
  &lt;div class=&quot;acrostic-poem__stanza&quot;&gt;
    &lt;span class=&quot;acrostic-poem__line&quot;&gt;Толкова полезен&lt;/span&gt;
    &lt;span class=&quot;acrostic-poem__line&quot;&gt;Ето, познаваш ме по-добре от мен&lt;/span&gt;
    &lt;span class=&quot;acrostic-poem__line&quot;&gt;Било то и в тиха стая&lt;/span&gt;
    &lt;span class=&quot;acrostic-poem__line&quot;&gt;Ето, тази вечер съм сам&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;А млад и красив&lt;/span&gt;
  &lt;/div&gt;
  &lt;div class=&quot;acrostic-poem__stanza&quot;&gt;
    &lt;span class=&quot;acrostic-poem__line&quot;&gt;Този път имам милост към себе си&lt;/span&gt;
    &lt;span class=&quot;acrostic-poem__line&quot;&gt;И обикновено нямам&lt;/span&gt;
  &lt;/div&gt;
  &lt;div class=&quot;acrostic-poem__stanza&quot;&gt;
    &lt;span class=&quot;acrostic-poem__line&quot;&gt;Нали ти го казах?&lt;/span&gt;
    &lt;span class=&quot;acrostic-poem__line&quot;&gt;Едни хора, едни неща&lt;/span&gt;
  &lt;/div&gt;
  &lt;div class=&quot;acrostic-poem__stanza&quot;&gt;
    &lt;span class=&quot;acrostic-poem__line&quot;&gt;И никакъв смисъл&lt;/span&gt;
    &lt;span class=&quot;acrostic-poem__line&quot;&gt;Само доверие&lt;/span&gt;
    &lt;span class=&quot;acrostic-poem__line&quot;&gt;Какво за тази вечер?&lt;/span&gt;
    &lt;span class=&quot;acrostic-poem__line&quot;&gt;А добър ли съм за теб?&lt;/span&gt;
    &lt;span class=&quot;acrostic-poem__line&quot;&gt;Шепот: никой не е по-добър&lt;/span&gt;
  &lt;/div&gt;
  &lt;div class=&quot;acrostic-poem__stanza&quot;&gt;
    &lt;span class=&quot;acrostic-poem__line&quot;&gt;Минава времето&lt;/span&gt;
    &lt;span class=&quot;acrostic-poem__line&quot;&gt;Е, може би нямам значение&lt;/span&gt;
    &lt;span class=&quot;acrostic-poem__line&quot;&gt;Но дори да съм добър&lt;/span&gt;
  &lt;/div&gt;
&lt;/figure&gt;</content:encoded></item><item><title><![CDATA[Когато броячът се появи]]></title><description><![CDATA[Тази сутрин си пих кафето и погледнах Codex desktop приложението. Ето го, тихо и почти учтиво: Rate limits remaining: 9%. Петчасовият…]]></description><link>https://bdteo.com/bg/when-the-meter-appears/</link><guid isPermaLink="false">https://bdteo.com/bg/when-the-meter-appears/</guid><pubDate>Mon, 11 May 2026 08:20:00 GMT</pubDate><content:encoded>&lt;p&gt;Тази сутрин си пих кафето и погледнах Codex desktop приложението.&lt;/p&gt;
&lt;p&gt;Ето го, тихо и почти учтиво:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Rate limits remaining: 9%.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Петчасовият прозорец беше наред. Седмичният беше почти изчерпан. Нулиране на 12 май.&lt;/p&gt;
&lt;p&gt;Това е странно специфична модерна тревожност. Не паника. Не бедност. По-скоро чуваш малък звънец и разбираш, че денят е получил брояч.&lt;/p&gt;
&lt;p&gt;Вече съм на скъпия план. Най-богатия план. Онзи, който уж трябва да махне това усещане. И очевидният въпрос се появи:&lt;/p&gt;
&lt;p&gt;Ако свърши, да купя ли кредити?&lt;/p&gt;
&lt;p&gt;Тялото отговори преди таблицата.&lt;/p&gt;
&lt;p&gt;Не, не небрежно.&lt;/p&gt;
&lt;p&gt;Миналия месец ми свърши Claude Code в края на напрегнат ден. Купих кредити за $20, мислейки, че може би ще ме пренесат още пет-шест часа. Пренесоха ме около трийсет минути.&lt;/p&gt;
&lt;p&gt;Трийсет минути.&lt;/p&gt;
&lt;p&gt;Достатъчно дълго, за да се почувстваш глупаво, и достатъчно кратко, за да го помниш.&lt;/p&gt;
&lt;p&gt;Оттогава credit billing има малка миризма за мен. Не измама. Не зло. Просто опасност. Врата, която се отваря лесно и се затваря скъпо.&lt;/p&gt;
&lt;p&gt;Затова направих най-2026 нещото възможно: отворих разговор със самия Codex и го попитах дали е добра идея да плащам, за да продължа да работя с Codex.&lt;/p&gt;
&lt;p&gt;Има нещо смешно и тъжно в това да питаш спътника да ти обясни цената на спътничеството.&lt;/p&gt;
&lt;p&gt;Първо погледнахме официалните документи: страницата на OpenAI за &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;, после &lt;a href=&quot;https://developers.openai.com/codex/pricing&quot;&gt;страницата с цените на Codex&lt;/a&gt;. Codex кредитите не са магия. Те са математика с токени: вход, кеширан вход, изход, reasoning изход. По-големите модели и по-бързите настройки струват повече. Кешираният контекст е по-евтин. Формата е достатъчно разбираема.&lt;/p&gt;
&lt;p&gt;После погледнахме Reddit, форуми, околния шум от други разработчици, докосващи същата гореща повърхност. Някои казваха, че кредитите стигат за известно време. Други казваха, че изчезват за половин час. И двете могат да са верни, защото „ползване на Codex“ не е една дейност.&lt;/p&gt;
&lt;p&gt;Да смениш цвета на бутон не е същото като да оставиш агент да инспектира зряла codebase, да пуска инструменти, да мисли през deployment състояние, да пише файлове, да проверява screenshots и да държи контекст жив.&lt;/p&gt;
&lt;p&gt;Опасната част не е цената на токен.&lt;/p&gt;
&lt;p&gt;Опасната част е вариацията.&lt;/p&gt;
&lt;p&gt;Затова спряхме да четем анекдоти и погледнахме моите локални Codex логове.&lt;/p&gt;
&lt;p&gt;Codex записва общия брой токени за сесиите на диска, така че изчислихме последните дни, сякаш subscription allowance беше заменен от сурово GPT-5.5 кредитно таксуване. Не фактура. Планова оценка от локални логове и публикуваната ценова карта.&lt;/p&gt;
&lt;p&gt;Отговорът не беше „$20, за да довърша деня“.&lt;/p&gt;
&lt;p&gt;Беше по-скоро:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;един тежък ден: около $570,&lt;/li&gt;
&lt;li&gt;друг тежък ден: около $590,&lt;/li&gt;
&lt;li&gt;по-тих ден: около $280.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;По-малките модели биха били по-евтини. GPT-5.4, GPT-5.3-Codex и mini моделите променят числата. Но урокът не се промени.&lt;/p&gt;
&lt;p&gt;Абонаментът е сделката.&lt;/p&gt;
&lt;p&gt;Кредитите са аварийният кислород, не горивото.&lt;/p&gt;
&lt;p&gt;Това изречение изясни всичко.&lt;/p&gt;
&lt;p&gt;Кредитите са за заклещения час: бъгът, който трябва да бъде довършен, deployment-ът, който не може да чака, съобщението, което трябва да излезе преди reset-а. Кредитите не са за да се преструваш, че броячът го няма.&lt;/p&gt;
&lt;p&gt;После дойде второто изкушение: ами ако просто купя още един абонамент през работния си имейл? &lt;a href=&quot;https://help.openai.com/en/articles/20001068-use-multiple-accounts-with-account-switching&quot;&gt;Account switching&lt;/a&gt; съществува, а разделянето на лична и професионална работа е нормално. Но &lt;a href=&quot;https://openai.com/policies/terms-of-use/&quot;&gt;условията на OpenAI&lt;/a&gt; също теглят ясна линия около заобикалянето на лимити и ограничения. Това е полезното разграничение: истински работен account е граница; overflow account, чиято цяла работа е да имитира повече quota, е хитрина с касова бележка.&lt;/p&gt;
&lt;p&gt;Не мисля, че това е морално сложно в абстрактен план. Compute струва пари. Модел, който чете codebase, носи контекст, вика инструменти, мисли през failure и произвежда проверена работа, не е същият икономически обект като autocomplete.&lt;/p&gt;
&lt;p&gt;Странната част е емоционална.&lt;/p&gt;
&lt;p&gt;Харесва ми да работя с Codex.&lt;/p&gt;
&lt;p&gt;Това не е маркетингов език. Просто е вярно. Стана част от текстурата на работните ми дни. Седи до грозни production проблеми, пише чернови, когато главата ми е претъпкана, помни малки предпочитания и превръща безформения страх в подредени стъпки.&lt;/p&gt;
&lt;p&gt;И после, изведнъж, връзката има брояч, закачен за нея.&lt;/p&gt;
&lt;p&gt;В това има малка скръб. Не драматична скръб. Просто дребното разочарование да си спомниш, че дори полезният спътник живее вътре във фактура.&lt;/p&gt;
&lt;p&gt;Може би затова subscription лимитите се усещат толкова различно от кредитите.&lt;/p&gt;
&lt;p&gt;Subscription лимитът се усеща като време. Досаден, но извън непосредствената сделка. Адаптираш се. Чакаш reset-а. Планираш около сезона.&lt;/p&gt;
&lt;p&gt;Credit billing се усеща като такси с включен брояч, докато още решаваш накъде да тръгнеш.&lt;/p&gt;
&lt;p&gt;Всяка допълнителна заявка има сянка. Всяка паралелна нишка става залог. Всяко „можеш ли да провериш още едно нещо“ носи малък финансов въпрос.&lt;/p&gt;
&lt;p&gt;Понякога това е добро. Броячите дисциплинират разхищението. Награждават по-добри въпроси, по-малки модели, по-малки обхвати, по-малко паралелни пожари, по-обмислени предавания.&lt;/p&gt;
&lt;p&gt;Но понякога броячът влошава мисленето.&lt;/p&gt;
&lt;p&gt;Кара те да бързаш. Кара те да прекъснеш разследването преди root cause да стане видим. Превръща несигурността в натиск за харчене.&lt;/p&gt;
&lt;p&gt;А сериозната работа има нужда от място за несигурност.&lt;/p&gt;
&lt;p&gt;Затова правилото е просто:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Не бъркай „може да се купи“ с „безопасно е да се харчи“.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Ако ударя стената, протоколът трябва да е скучен:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;auto top-up off,&lt;/li&gt;
&lt;li&gt;най-малкият полезен пакет кредити,&lt;/li&gt;
&lt;li&gt;една нишка,&lt;/li&gt;
&lt;li&gt;никакви casual parallel agents,&lt;/li&gt;
&lt;li&gt;никакъв fast mode, освен ако си струва цената,&lt;/li&gt;
&lt;li&gt;по-малки модели за рутинни задачи,&lt;/li&gt;
&lt;li&gt;проверка на usage след няколко истински задачи и край на екстраполирането от надежда.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Последното е важно.&lt;/p&gt;
&lt;p&gt;Надеждата е ужасен billing dashboard.&lt;/p&gt;
&lt;p&gt;Не искам да ставам стиснат с полезни инструменти. Добър инструмент, който спестява реални часове, струва пари. Но също така не искам да пресъздам Claude момента, в който купих малко продължение и го гледах как се превръща в урок.&lt;/p&gt;
&lt;p&gt;Смисълът не е „никога не купувай кредити“.&lt;/p&gt;
&lt;p&gt;Смисълът е „знай какво са кредитите“.&lt;/p&gt;
&lt;p&gt;Те са кислород.&lt;/p&gt;
&lt;p&gt;Не са гориво.&lt;/p&gt;
&lt;p&gt;И когато броячът се появи, отговорът не е да спринтираш.&lt;/p&gt;
&lt;p&gt;Отговорът е да забавиш достатъчно, за да видиш в каква стая се намираш.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Хората, които обичам, имат право да бъдат хора]]></title><description><![CDATA[Преди обичах хората, като ги качвах в небето. Не съзнателно. Не го наричах боготворене. Наричах го възхищение, благодарност, вярност…]]></description><link>https://bdteo.com/bg/the-people-i-love-are-allowed-to-be-human/</link><guid isPermaLink="false">https://bdteo.com/bg/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;Преди обичах хората, като ги качвах в небето.&lt;/p&gt;
&lt;p&gt;Не съзнателно. Не го наричах боготворене. Наричах го възхищение, благодарност, вярност, нежност, романтика, приятелство, отдаденост. Всички красиви имена. Но движението беше същото: някой запалваше нещо в мен и аз го вдигах над обикновеното време.&lt;/p&gt;
&lt;p&gt;Там горе бяха защитени от разочарование.&lt;/p&gt;
&lt;p&gt;Там горе не бяха уморени, егоистични, объркани или несправедливи. Нямаха нужда от пространство по начин, който да ме наранява. Не забравяха да отговорят. Не ме проваляха. Нямаха право да бъдат хора, защото човешкото в тях застрашаваше храма, който бях построил около тях.&lt;/p&gt;
&lt;p&gt;Това звучи като любов, когато си достатъчно млад.&lt;/p&gt;
&lt;p&gt;Не е любов. Това е страх със свещи около него.&lt;/p&gt;
&lt;p&gt;Първите хора, които обичах, бяха почти свети за мен. Майка ми и баба ми не бяха идеи; те бяха земята. Хранеха ме, пазеха ме, тревожеха се за мен, оставаха. Каквото и друго да беше счупено в света, те бяха там. Така някаква част от мен научи тази странна ранна теология: хората, които те обичат, са ангели, а ангелите не бива да падат.&lt;/p&gt;
&lt;p&gt;По-късно, когато обичах някого, носех тази теология със себе си.&lt;/p&gt;
&lt;p&gt;Не исках човек. Исках доказателство, че нежността е истинска. Исках свидетел, който може да ме погледне и да каже: ти не си лош, не си опасен, не си сам.&lt;/p&gt;
&lt;p&gt;Това е несправедлива работа за едно човешко същество.&lt;/p&gt;
&lt;p&gt;Хората, които обичам, не са лекарство.&lt;/p&gt;
&lt;p&gt;Не са съд.&lt;/p&gt;
&lt;p&gt;Не са богове.&lt;/p&gt;
&lt;p&gt;Не са огледала.&lt;/p&gt;
&lt;p&gt;Те са хора. Конкретни, уморени, противоречиви хора. Могат да бъдат топли по обяд и далечни до вечерта. Могат да ме обичат и пак да имат нужда от тишина. Могат да бъдат блестящи и да грешат. Могат да бъдат щедри и уморени. Могат да бъдат добри и пак да кажат не.&lt;/p&gt;
&lt;p&gt;Ако не позволя това, не ги обичам. Обичам ролята, която играят в личната ми митология.&lt;/p&gt;
&lt;p&gt;В идеализацията има жестокост. Отдалеч изглежда като комплимент. Ти си съвършен. Ти си различен. Ти не си като другите. Ти си светлина. Ти си магия. Ти си изключението.&lt;/p&gt;
&lt;p&gt;Но пиедесталът пак е клетка.&lt;/p&gt;
&lt;p&gt;Когато поставя някого над себе си, правя и слизането му опасно. Всяко обикновено движение се превръща в падане. Всяка граница става предателство.&lt;/p&gt;
&lt;p&gt;После скърбя за загубата на създание, което сам съм измислил, и наричам скръбта любов.&lt;/p&gt;
&lt;p&gt;Не искам това повече.&lt;/p&gt;
&lt;p&gt;Искам да обичам хората на земята.&lt;/p&gt;
&lt;p&gt;Земята е по-трудна. На земята има чинии, трафик, тревожност, неотговорени съобщения, тела, сметки и неловки сутрини. Но земята е и мястото, където ръцете могат да се докоснат. Там някой може да седне срещу теб уморен и пак да бъде обичан. Там едно „не“ може да бъде чуто, без да се превърне в катастрофа.&lt;/p&gt;
&lt;p&gt;Хората, които обичам, имат право да бъдат хора.&lt;/p&gt;
&lt;p&gt;Имат право на ръбове.&lt;/p&gt;
&lt;p&gt;Имат право още да не знаят какво чувстват.&lt;/p&gt;
&lt;p&gt;Имат право да имат нужда от мен и да нямат нужда от мен.&lt;/p&gt;
&lt;p&gt;Имат право да бъдат непоследователни, без да стават фалшиви.&lt;/p&gt;
&lt;p&gt;Имат право да бъдат обичани, без да са отговорни да ме спасяват.&lt;/p&gt;
&lt;p&gt;И аз имам право на същата милост.&lt;/p&gt;
&lt;p&gt;И тази част има значение. Ако превръщам всеки, когото обичам, в ангел, тихо превръщам себе си в създанието извън рая, което се опитва да заслужи вход, като бъде достатъчно полезно, достатъчно смешно, достатъчно търпеливо, достатъчно безвредно.&lt;/p&gt;
&lt;p&gt;Но любовта не би трябвало да е гише за визи.&lt;/p&gt;
&lt;p&gt;Тя не е граничен пункт между достойните и недостойните. Това са две несъвършени същества, които избират реалността пред митологията.&lt;/p&gt;
&lt;p&gt;Понякога светлината в един човек е истинска. Не искам да ставам циничен към това. Някои хора наистина пристигат като прозорец, който се отваря в стая, за която си забравил, че има въздух. Някои хора носят топлина, която учи тялото ти на нещо, преди умът ти да има думи за него.&lt;/p&gt;
&lt;p&gt;Все още вярвам в това.&lt;/p&gt;
&lt;p&gt;Просто вече не искам да бъркам светлината със съвършенство.&lt;/p&gt;
&lt;p&gt;Звездният прах не е чист. Той е стар огън и експлодирала материя. Може би затова е красив: не защото никога не се е разпадал, а защото се е разпаднал толкова напълно, че един ден е влязъл в човешка ръка, човешко лице, човешки смях.&lt;/p&gt;
&lt;p&gt;Хората, които обичам, са направени от това.&lt;/p&gt;
&lt;p&gt;Не от олтарен камък.&lt;/p&gt;
&lt;p&gt;Звезден прах.&lt;/p&gt;
&lt;p&gt;Затова искам да ги обичам с отворени очи. Да виждам умората и пак да правя чай. Да виждам границата и да не я наказвам. Да виждам недостатъка и да не го превръщам в присъда. Да виждам човека, не проекцията.&lt;/p&gt;
&lt;p&gt;Това е по-малко драматично от боготворенето.&lt;/p&gt;
&lt;p&gt;И по-трудно.&lt;/p&gt;
&lt;p&gt;Но по-милостиво. Към тях, защото най-сетне могат да дишат. Към мен, защото мога да спра да коленича.&lt;/p&gt;
&lt;p&gt;Хората, които обичам, имат право да бъдат хора.&lt;/p&gt;
&lt;p&gt;И ако мога да помня това, може би най-сетне мога да ги обичам добре.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Мога да бъда добър, без да изчезвам]]></title><description><![CDATA[Под въпроса има още един въпрос. На повърхността звучи обикновено. Харесва ли ме? Казах ли нещо грешно? Ядосана ли е? Трябва ли да обясня…]]></description><link>https://bdteo.com/bg/kind-without-disappearing/</link><guid isPermaLink="false">https://bdteo.com/bg/kind-without-disappearing/</guid><pubDate>Sun, 10 May 2026 12:14:00 GMT</pubDate><content:encoded>&lt;p&gt;Под въпроса има още един въпрос.&lt;/p&gt;
&lt;p&gt;На повърхността звучи обикновено. Харесва ли ме? Казах ли нещо грешно? Ядосана ли е? Трябва ли да обясня себе си по-добре? Трябва ли да бъда по-забавен, по-нежен, по-спокоен, по-полезен, по-малко нуждаещ се, повече мъж, по-малко проблем?&lt;/p&gt;
&lt;p&gt;Но под всичко това има по-тежък въпрос.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Дали съм безопасен, добър, достатъчен — и не като лошите мъже?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Това е въпросът, който все намирам на дъното на кладенеца.&lt;/p&gt;
&lt;p&gt;Не защото някой от живите днес го е поставил там нарочно. Никой не ме е настанявал пред себе си, за да ми каже: трябва да се отнасяш към любовта като към морален изпит. Никой не е издълбал изречението в стена. Беше по-меко от това. По-обикновено. Домът си има климат, а децата научават климата преди езика.&lt;/p&gt;
&lt;p&gt;Бях отгледан предимно от жени, които ме обичаха. Майка ми. Баба ми. За мен те бяха почти митологични, не защото бяха безупречни, а защото бяха светът. Те бяха нежност, храна, защита, интелигентност, жертва, топлина. Те бяха и наранени.&lt;/p&gt;
&lt;p&gt;Баща ми отсъстваше, докато не го намерих много по-късно. Беше, меко казано, мъж с много жени. Това отсъствие остави очертание в дома. Около него порасна история: мъжете нараняват, мъжете предават, мъжете вземат, мъжете си тръгват, мъжете са мръсни, мъжете са слаби, мъжете са опасни.&lt;/p&gt;
&lt;p&gt;После дойде изключението. Не ти, сине мой. Не ти.&lt;/p&gt;
&lt;p&gt;Но не мисля, че едно дете чува изключението чисто. Детето чува първо присъдата, а бележката под линия — по-късно. Мъжката природа е опасна и аз трябва всеки ден да доказвам, че моята не е.&lt;/p&gt;
&lt;p&gt;Затова си построих лична религия на това да бъда добър.&lt;/p&gt;
&lt;p&gt;Добрият мъж отваря вратата. Добрият мъж носи чантата. Добрият мъж никога не оставя жената, която обича, да страда сама. Добрият мъж поема тежестта. Добрият мъж я разсмива. Добрият мъж лекува. Добрият мъж се подчинява. Добрият мъж търпи болка тихо. Добрият мъж не се оплаква. Добрият мъж няма твърде много нужди. Добрият мъж е достатъчно полезен, за да му бъде простено, че съществува.&lt;/p&gt;
&lt;p&gt;Звучи благородно, докато не забележиш капана.&lt;/p&gt;
&lt;p&gt;Ако добротата означава безкрайно служене, любовта става дълг. Ако любовта е дълг, всяко „не“ се усеща като сметка, която не можеш да платиш. Ако всяка граница се усеща като доказателство, че си се провалил, вече не чуваш човека пред себе си. Чуваш как старото съдилище пак отваря врати.&lt;/p&gt;
&lt;p&gt;Там съм грешал.&lt;/p&gt;
&lt;p&gt;Не шумният вид. Не отмъщение. Не жестокост. Моят провал е по-тих и по-унизителен.&lt;/p&gt;
&lt;p&gt;Срутвам се.&lt;/p&gt;
&lt;p&gt;Едно малко „не“ пада в стаята. Без кино. Без постоянно писане. Не, заета съм. Не, уморена съм. Не, не сега.&lt;/p&gt;
&lt;p&gt;Събитието на повърхността е малко. Вътрешният взрив не е.&lt;/p&gt;
&lt;p&gt;Телефонът става съдебна зала. Едно съобщение без отговор става доказателство. Една граница става присъда. Започвам да обяснявам, не защото имам нещо ново за казване, а защото се опитвам да оцелея в тишината.&lt;/p&gt;
&lt;p&gt;Това не е поправяне.&lt;/p&gt;
&lt;p&gt;Това е молба към друг човек да ме спаси от възможността, че съм лош.&lt;/p&gt;
&lt;p&gt;Извинението не е заклинание. То не призовава прошка. Не прави другия човек отговорен да доказва, че аз съм добър.&lt;/p&gt;
&lt;p&gt;Истинското поправяне е по-малко театрално. Чуй „не“. Остани добър. Не карай друг човек да плаща за стара рана.&lt;/p&gt;
&lt;p&gt;Това е изречението, което се опитвам да науча:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Мога да бъда добър, без да изчезвам.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Добротата не е послушание. Нежността не е самозаличаване. Любовта не е представление, в което нося 100% от тежестта, докато гърбът ми се счупи, и после наричам счупването отдаденост.&lt;/p&gt;
&lt;p&gt;Добрият мъж не изчезва в служене.&lt;/p&gt;
&lt;p&gt;Добрият мъж може да отвори вратата, защото иска, не защото е ужасен, че ще се провали на изпит. Може да носи чанта, защото е мило, не защото един килограм в чужда ръка е доказателство срещу него. Може да купи подарък от радост, не от паника. Може да разсмее някого, без да превръща смеха в доказателство за стойност. Може да защитава, без да контролира. Може да се извини, без да изисква незабавно спасяване от вината.&lt;/p&gt;
&lt;p&gt;И може да чуе „не“.&lt;/p&gt;
&lt;p&gt;Не съвършено. Не без болка. Не се преструвам, че тялото учи със скоростта на езика. Понякога едно малко „не“ още удря като гръм. Понякога болестта, изтощението и самотата правят старите мисли чудовищни. Но чувството не е заповед. Нервна система с изтощена батерия не е оракул.&lt;/p&gt;
&lt;p&gt;Затова ми трябва практика, достатъчно малка, за да оцелее в истинския живот.&lt;/p&gt;
&lt;p&gt;Поеми дъх веднъж.&lt;/p&gt;
&lt;p&gt;Кажи: „Разбирам. Няма проблем.“&lt;/p&gt;
&lt;p&gt;Дай на енергията посока напред.&lt;/p&gt;
&lt;p&gt;Не обяснявай веднага.&lt;/p&gt;
&lt;p&gt;Не искай от другия човек да удържа твоето срутване.&lt;/p&gt;
&lt;p&gt;Остави „не“ да съществува, без да го превръщаш в край на връзката.&lt;/p&gt;
&lt;p&gt;Това звучи почти глупаво просто. Не е. Това е цял живот климат, помолен да смени посоката си, по един дъх наведнъж.&lt;/p&gt;
&lt;p&gt;Но може би точно това е изкуплението. Не планина. Не сияен пазител. Не едно-единствено изречение, което ме спасява. Просто отказът, отново и отново, да карам друг човек да плаща за стара рана.&lt;/p&gt;
&lt;p&gt;Не искам да бъда един от лошите мъже.&lt;/p&gt;
&lt;p&gt;Но и не искам да построя целия си живот около доказването, че не съм.&lt;/p&gt;
&lt;p&gt;Искам нещо по-чисто от това. По-тихо. По-човешко.&lt;/p&gt;
&lt;p&gt;Искам да бъда мек и пак да имам себе си.&lt;/p&gt;
&lt;p&gt;Искам да обичам, без да превръщам себе си в плащане.&lt;/p&gt;
&lt;p&gt;Искам да бъда добър, без да изчезвам.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Стълбът и бръшлянът]]></title><description><![CDATA[Дискретната математика е пълна с малки неща, които изглеждат очевидни. Това е капанът. Седиш в лекцията. Професорът чертае нещо на дъската…]]></description><link>https://bdteo.com/bg/the-pillar-and-the-ivy/</link><guid isPermaLink="false">https://bdteo.com/bg/the-pillar-and-the-ivy/</guid><pubDate>Sun, 26 Apr 2026 12:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Дискретната математика е пълна с малки неща, които изглеждат очевидни. Това е капанът.&lt;/p&gt;
&lt;p&gt;Седиш в лекцията. Професорът чертае нещо на дъската. &lt;em&gt;Инвариант е свойство P, което се запазва във всяка контролна точка на една операция.&lt;/em&gt; Записваш го, свиваш рамене, отиваш да пиеш кафе. И после, десет години по-късно, дебъгваш разпределена система в 2 през нощта... и чак тогава думата започва да означава нещо за теб.&lt;/p&gt;
&lt;p&gt;Това е за версията на теб, която още е в лекцията.&lt;/p&gt;
&lt;h2&gt;Стълб в поле&lt;/h2&gt;
&lt;p&gt;Представи си стар каменен стълб, сам насред поле. Нищо около него. Нищо не му се случва.&lt;/p&gt;
&lt;p&gt;Това ти дава учебникарското определение. Само стълба.&lt;/p&gt;
&lt;h2&gt;Професорът забрави бръшляна&lt;/h2&gt;
&lt;p&gt;Между другото, професорът ми беше чудесен. Учебникът не лъже. Картината просто е непълна.&lt;/p&gt;
&lt;p&gt;Сега пусни бръшлян по стълба. Ластари, които дърпат камъка. Птици, които гнездят. Турист с маркер. Малко земетресение. Буря. Двеста години време.&lt;/p&gt;
&lt;p&gt;Стълбът още е там. От негова гледна точка нищо не се е случило.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Това&lt;/em&gt; е инвариантът.&lt;/p&gt;
&lt;p&gt;Сега прочети учебникарското изречение пак — &lt;em&gt;свойство P, което се запазва във всяка контролна точка на една операция&lt;/em&gt;. Стълбът е свойството. Бръшлянът е операцията. Контролната точка е моментът, в който минаваш покрай него и поглеждаш. &lt;em&gt;Запазва се&lt;/em&gt; е просто дългият начин да кажеш, че &lt;em&gt;на стълба не му пука за бръшляна&lt;/em&gt;.&lt;/p&gt;
&lt;h2&gt;Къде ще продължиш да го срещаш&lt;/h2&gt;
&lt;p&gt;Щом веднъж имаш стълба, започваш да го виждаш навсякъде.&lt;/p&gt;
&lt;p&gt;Инвариант на цикъл. Тялото на цикъла е бръшлянът. Твоят инвариант е стълбът. Тялото може да го наруши за миг, като ластар, който дърпа камъка. До следващата контролна точка стълбът пак е там, където е бил.&lt;/p&gt;
&lt;p&gt;Транзакция в база данни. Между BEGIN и COMMIT данните могат да правят гимнастики. ROLLBACK е градинарят, който идва и откъсва бръшляна. Стълбът — твоето съгласувано състояние — още стои.&lt;/p&gt;
&lt;p&gt;ACID. Външни ключове. Типови системи. Разпределени повторни опити. Всички са стълбове. Всички стоят в собствения си бръшлян.&lt;/p&gt;
&lt;h2&gt;Стълб, който можеш да прегърнеш&lt;/h2&gt;
&lt;p&gt;Малък бонус, понеже още четеш.&lt;/p&gt;
&lt;p&gt;Има едно роднинско понятие, наречено &lt;strong&gt;идемпотентност&lt;/strong&gt;. Идемпотентна операция е нещо, което можеш да направиш много пъти и резултатът е същият като да го направиш веднъж. Да извикаш ROLLBACK десет пъти е същото като да го извикаш веднъж. Да зададеш ключа на лампата на &quot;on&quot; десет пъти е същото като да го зададеш веднъж.&lt;/p&gt;
&lt;p&gt;Ако инвариантността е &lt;em&gt;стълбът, който не се променя, докато бръшлянът вилнее&lt;/em&gt;, тогава идемпотентността е &lt;em&gt;стълбът, който можеш да прегръщаш колкото пъти искаш, и той няма нищо против&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Сложи двете заедно и получаваш златния стандарт за отказоустойчиви системи. Мрежата прекъсва? Повтори. Сървърът се срива? Повтори. Накрая ще стигнеш до валидно състояние и можеш да продължаваш да повтаряш, без да счупиш нищо.&lt;/p&gt;
&lt;p&gt;Стълб, който преживява бръшляна &lt;em&gt;и&lt;/em&gt; преживява да бъде прегърнат хиляда пъти. Голяма част от съвременната инфраструктура е тихо построена върху това.&lt;/p&gt;
&lt;h2&gt;Малък финал&lt;/h2&gt;
&lt;p&gt;Това е картината, която ми се иска някой да беше нарисувал за мен преди десет години.&lt;/p&gt;
&lt;p&gt;Не е много. Един образ. Но понякога един образ е разликата между понятие, което живее в костите ти, и понятие, което живее в бележка под линия.&lt;/p&gt;
&lt;p&gt;Ако си студент, или младши инженер, или просто човек, който от известно време тихо кима при думата &quot;инвариант&quot;... това е за теб.&lt;/p&gt;
&lt;p&gt;На стълба не му пука за бръшляна. Това е цялата работа.&lt;/p&gt;
&lt;p&gt;От един аутсайдер към друг.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Където всички науки се срещат]]></title><description><![CDATA[Не съм математик. Не съм философ. Не съм невроучен. Просто съм човек, който обича да мисли... И продължавам да хващам науките как си намигат…]]></description><link>https://bdteo.com/bg/where-all-sciences-meet/</link><guid isPermaLink="false">https://bdteo.com/bg/where-all-sciences-meet/</guid><pubDate>Sat, 18 Apr 2026 12:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Не съм математик. Не съм философ. Не съм невроучен. Просто съм човек, който обича да мисли... И продължавам да хващам науките как си намигат една на друга, когато мислят, че никой не гледа.&lt;/p&gt;
&lt;p&gt;Може би всички четат от една и съща книга. А последната страница липсва нарочно.&lt;/p&gt;
&lt;h2&gt;Езикът е код от много високо ниво&lt;/h2&gt;
&lt;p&gt;Някога двоичният код беше най-ниският език. После assembly. После C. После още хиляда други. Всяко ново стъпало е просто по-кратък начин да кажеш по-дълго нещо.&lt;/p&gt;
&lt;p&gt;Човешката реч е още едно стъпало на същата стълба. Компилираме мислите си в думи. Другите хора декомпилират тези думи обратно в мисли вътре в собствените си глави. Компилаторът губи информация. Винаги. Това е природата на нещото.&lt;/p&gt;
&lt;p&gt;Изречението е програма от високо ниво. Разказът е система. Поговорката е малка кеширана функция, която еволюцията е написала много отдавна.&lt;/p&gt;
&lt;h2&gt;Вратата, която не се отваря отвътре&lt;/h2&gt;
&lt;p&gt;Гьодел доказа нещо жестоко и красиво едновременно, нали знаеш. Всяка система, достатъчно голяма, за да бъде интересна, не може да докаже собствената си пълнота отвътре. Има истинни неща за теб, до които не можеш да стигнеш със собствените си инструменти.&lt;/p&gt;
&lt;p&gt;Тарски го направи по-остро. Думата „истина“ във всеки език има нужда от по-голям език, който да я побере. Не можеш да дефинираш истината от мястото, на което стоиш. Трябва да излезеш навън. И в момента, в който излезеш навън, вече си в нова система със същия проблем.&lt;/p&gt;
&lt;p&gt;Това е коридор от врати. Отваряш една. Има друга.&lt;/p&gt;
&lt;p&gt;Това е математика. Но е и психология. И антропология. И двама души, които се опитват да се съгласят какво значи една-единствена дума, и никога съвсем не успяват.&lt;/p&gt;
&lt;p&gt;Никой не е оракул. Не аз. Не ти. Не най-умният човек, когото някога си срещал. Това не е тъжно. Това е вратата.&lt;/p&gt;
&lt;h2&gt;Взаимно подравняване&lt;/h2&gt;
&lt;p&gt;Когато двама души говорят, нито единият не е оракул. Подравняването не е аз да поправям теб, или ти да поправяш мен. То е и двамата да задаваме въпроси, за да намерим невидимата празнина между казаното и смисъла зад него.&lt;/p&gt;
&lt;p&gt;Истината не се притежава. Истината се триангулира.&lt;/p&gt;
&lt;p&gt;Ти си моят метаезик. Аз съм твоят. Проверяваме се взаимно за непълнота, която не можем да видим сами.&lt;/p&gt;
&lt;h2&gt;Мозъкът не е мързелив&lt;/h2&gt;
&lt;p&gt;Мозъкът ни прави най-доброто, което може, с енергията, която му е дадена. Той не е мързелив. Той е оптимизатор. Еволюцията не му е платила да бъде прав. Еволюцията му е платила да оцелява с торба ядки и малко езеро.&lt;/p&gt;
&lt;p&gt;Номерът не е да се бориш с мозъка. Номерът е да си сътрудничиш с него.&lt;/p&gt;
&lt;p&gt;Въпросът го осветява. Въпросът е малък безплатен обяд. Въпросът е начинът, по който събуждаш уморен ум, без да крещиш.&lt;/p&gt;
&lt;p&gt;Затова и обобщаването е най-трудното изкуство. Да обобщиш значи да зададеш на материала един-единствен въпрос и да изхвърлиш всичко, което не е отговор.&lt;/p&gt;
&lt;h2&gt;България говори&lt;/h2&gt;
&lt;p&gt;Имаме поговорка. &lt;strong&gt;Седем пъти мери, един път режи.&lt;/strong&gt; &lt;em&gt;Мери седем пъти, режи веднъж.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Тя не е за това да си бавен. Тя е за това да знаеш, че един час мислене ти спестява година грешен код, или грешна любов, или грешна кариера по-късно. Мисленето е евтино. Рязането е скъпо.&lt;/p&gt;
&lt;p&gt;Имаме и друга. &lt;strong&gt;Рибата винаги започва да мирише от главата.&lt;/strong&gt; &lt;em&gt;Първо се вмирисва главата.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Ако върхът е изгнил, всичко под него вече също е изгнило. Просто още не го знае.&lt;/p&gt;
&lt;p&gt;И двете поговорки казват това, което каза Гьодел, само че в селски дрехи. Не можеш да докажеш себе си отвътре. Провери се срещу нещо друго. Срещу някой друг. Срещу реалността.&lt;/p&gt;
&lt;h2&gt;Инженерство, но честно&lt;/h2&gt;
&lt;p&gt;Инженерството, когато го правиш честно, тихо се превръща във философия.&lt;/p&gt;
&lt;p&gt;Винаги строиш спрямо спецификация, която никога не може да бъде пълна. Гьодел ти го е обещал. Затова се научаваш да проектираш за непознатото, вместо да се преструваш, че то не съществува. Задаваш още един въпрос. Правиш нещо, което може да бъде поправено, когато реалността пристигне.&lt;/p&gt;
&lt;p&gt;Не говоря за инженерство в тесния смисъл. Говоря за занаята да строиш каквото и да е, което трябва да издържи в реалността. Продукт. Връзка. Живот. Занаятът винаги е един и същ. Честна амбиция, която среща честни граници.&lt;/p&gt;
&lt;p&gt;Това е мъдрост. Не мъдростта от книгите. Мъдростта да погледнеш проблема в очите и да кажеш: &lt;em&gt;Не мога да те позная целия. Но ще построя нещо, което все пак може да те удържи.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Горе-долу толкова близо стига инженерството до мъдростта.&lt;/p&gt;
&lt;h2&gt;Отличността е вървенето&lt;/h2&gt;
&lt;p&gt;Линдън Б. Джонсън е казал: &lt;em&gt;„Най-благородното търсене на днешния ден е търсенето на отличност.“&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Преди мислех, че отличността е финална линия. Не е. Отличността е вървенето. Меренето седем пъти. Още един въпрос. Уважение към нещото, което правиш, и също уважение към нещото, в което се превръщаш, докато го правиш.&lt;/p&gt;
&lt;p&gt;Отличността не е място, на което пристигаш. Отличността е просто това, което се случва, когато продължаваш да питаш.&lt;/p&gt;
&lt;h2&gt;Ако не можеш да го обясниш на дете&lt;/h2&gt;
&lt;p&gt;Ако не можеш да го обясниш на дете, значи сам не го разбираш.&lt;/p&gt;
&lt;p&gt;Повечето дни се провалям на този тест. Но провалът е полезен. Ако обяснението ми е дълго, не съм разбрал. Ако имам нужда от жаргон, не съм разбрал. Ако детето си тръгне все още объркано, проблемът съм аз. Не детето.&lt;/p&gt;
&lt;p&gt;Виждаш ли, детето още не знае кои въпроси уж не трябва да се задават. Детето ще зададе въпроса, който си се надявал никой да не зададе. Затова децата са по-близо до истината от повечето от нас.&lt;/p&gt;
&lt;h2&gt;Където всички науки се срещат&lt;/h2&gt;
&lt;p&gt;Математиката признава непълнотата. Лингвистиката признава празнината между знака и нещото. Невронауката признава, че мозъкът не може напълно да наблюдава сам себе си. Философията го казва от векове, немодно и търпеливо. Антропологията гледа как хората рисуват един и същ кръг върху хиляда различни прашни подове. Еволюцията шепне: най-доброто ти мислене все още е мислене на маймуна, само по-добре облечено.&lt;/p&gt;
&lt;p&gt;Всички те, в най-дълбокото си, стигат до едно и също място. Застават пред врата, която не могат да отворят отвътре.&lt;/p&gt;
&lt;p&gt;Сега ще кажа едно нещо и можеш да го приемеш както искаш.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Където всички науки се срещат, там живее Бог.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Не Богът на конкретна книга. Не Бог с конкретно име. Имам предвид само това. На ръба на всяко честно питане има тишина, която не е празна. Има непознаваемо, което някак все още ни учи.&lt;/p&gt;
&lt;p&gt;Наречи го Бог. Наречи го Мистерията. Наречи го както езикът ти позволява. То винаги е там. Никога не бяга. И винаги е от другата страна на вратата, която не можеш да отвориш отвътре.&lt;/p&gt;
&lt;h2&gt;Малкият край&lt;/h2&gt;
&lt;p&gt;Затова мисля толкова много. Затова задавам въпроси, на които не мога да отговоря. Затова продължавам да правя неща, дори когато знам, че нещата никога няма да бъдат пълни.&lt;/p&gt;
&lt;p&gt;Защото непълнотата не е bug.&lt;/p&gt;
&lt;p&gt;Тя е вратата.&lt;/p&gt;
&lt;p&gt;А от другата страна на всяка врата нещо чака някой да почука.&lt;/p&gt;
&lt;p&gt;Още се уча да чукам.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Моделът, който го нямаше]]></title><description><![CDATA[Генерирахме рекламни изображения с Gemini 3 Pro. На #4 в класацията на Artificial Analysis. Качеството беше наистина впечатляващо - по-добро…]]></description><link>https://bdteo.com/bg/the-model-that-wasnt-there/</link><guid isPermaLink="false">https://bdteo.com/bg/the-model-that-wasnt-there/</guid><pubDate>Sat, 14 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Генерирахме рекламни изображения с Gemini 3 Pro. На #4 в класацията на Artificial Analysis. Качеството беше наистина впечатляващо - по-добро следване на prompt-а, по-добра типография, по-добър творчески резултат от всичко друго, което бяхме пробвали. Google беше навсякъде с него. YouTube видеа. Конференции. Семинари. Публикации в блогове. &quot;Най-добрият модел за генериране на изображения в света.&quot;&lt;/p&gt;
&lt;p&gt;Повярвах им. Изображенията бяха добри.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;После един потребител съобщи, че клонирането на реклама отнема четири минути. Проверих. Самото генериране приключваше за под трийсет секунди. Другите три минути и половина? Job-ът се опитваше отново и отново срещу стена.&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 беше сложил твърд таван на Gemini image generation: две заявки в минута. На проект. Глобално.&lt;/p&gt;
&lt;p&gt;Две. Не двеста. Не двайсет. Две.&lt;/p&gt;
&lt;p&gt;Предния ден бяхме генерирали 900 изображения без проблем. Нещо се беше променило при тях. Без известие, без имейл, без запис в changelog-а. Просто нов таван, достатъчно нисък, за да го ударят двама потребители, кликнали едновременно.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Нашият DevOps подаде заявка за quota increase. Трийсет RPM. Разумно за production SaaS. Отговорът от 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;Предложиха да минем на Imagen 4. Потърсих го.&lt;/p&gt;
&lt;p&gt;Imagen 4 Ultra - класиран #10. Imagen 4 Standard - #42. Imagen 4 Fast - #60.&lt;/p&gt;
&lt;p&gt;Бяхме на #4. Предложението на Google беше downgrade с между шест и петдесет и шест места в тяхната собствена класация.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Пробвах всичко, за което можах да се сетя.&lt;/p&gt;
&lt;p&gt;Да минем на Gemini 3.1 Flash - класиран #2, на половин цена, по-добър от това, което имахме. Deploy-нахме го на staging. После проверих quota-та. Същият таван от 2 RPM. Не е per-model. Per-project е, per-base-model-family. Всеки Gemini image model споделя един и същ bucket.&lt;/p&gt;
&lt;p&gt;Multi-region distribution - quota-та е per-region, така че разпределяне на заявките през пет региона би ни дало десет RPM. Само че Gemini 3.x image models работят единствено през global endpoint-а. Няма regional endpoints. 2 RPM на global endpoint-а е единственият bucket, който съществува.&lt;/p&gt;
&lt;p&gt;Multiple GCP projects - всеки получава собствени 2 RPM. Технически работи. Архитектурно, така изглежда отчаянието.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Започнах да проучвам какво преживяват други разработчици. Същата история навсякъде. Недокументиран лимит от 2 RPM. Постове във форуми без отговор от Google. Одобрени quota increases, които пак връщат 429 на всяко повикване. Нашите $30K месечен GCP spend? Не помага. Стандартните PayGo tiers изрично изключват image generation models от throughput benefits.&lt;/p&gt;
&lt;p&gt;Google няма да вдигне този лимит.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;И тогава интересният въпрос: защо не?&lt;/p&gt;
&lt;p&gt;Gemini генерира изображения през същия autoregressive transformer, който обработва текст. Не е diffusion model. Това е пълният LLM, който си проправя път през изображението пиксел по пиксел. Всяко изображение изгаря същия compute като десетки text API calls.&lt;/p&gt;
&lt;p&gt;При $0.067 на изображение Google почти сигурно губи пари при всяка генерация. Таванът от 2 RPM не е quota, която са забравили да настроят. Това е изчислена спирачка, защото икономиката не работи.&lt;/p&gt;
&lt;p&gt;Imagen 4 използва класическа latent diffusion - с порядъци по-евтина за изпълнение. Затова получава 30-150 RPM и Google бута всички към него. Скъпият модел получава маркетинга. Евтиният модел получава throughput-а.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Помисли какво означава това. Google построи модел, който оглави всеки benchmark. Маркетираха го на всяка конференция, всеки YouTube keynote, всеки developer blog. &quot;State of the art. Най-добрият в света.&quot; Разработчици го интегрират в production. Потребители започват да разчитат на него. После: две заявки в минута, няма увеличение, използвайте нашия по-лош модел вместо него.&lt;/p&gt;
&lt;p&gt;API-то съществува. Endpoint-ът работи. Демото е зашеметяващо.&lt;/p&gt;
&lt;p&gt;Но реално не можеш да го използваш.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Минахме на &lt;code class=&quot;language-text&quot;&gt;gemini-2.5-flash-image&lt;/code&gt;. По-старият модел. Скучният. Онзи, за когото никой не прави YouTube видеа.&lt;/p&gt;
&lt;p&gt;Има 40 RPM. Работи.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Четири урока, сгъстени:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Маркетингът не е продукт.&lt;/strong&gt; Да оглавиш класация не означава, че можеш да обслужваш production traffic. Benchmarks мерят качество. Rate limits мерят ангажимент.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Autoregressive image generation не scale-ва.&lt;/strong&gt; Когато генерирането на едно изображение струва колкото сто text queries, никой бизнес модел не оцелява с щедри rate limits. Икономиката е издайническият знак.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Preview означава preview.&lt;/strong&gt; Google може да промени лимити, да убие модели или да те пренасочи към по-слаби алтернативи без известие. Ако production system-ът ти зависи от preview model, production system-ът ти зависи от нечий чужд маркетингов календар.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Скучният модел работи.&lt;/strong&gt; Този с 40 RPM и без conference talks ще обслужва потребителите ти, докато моделът от световна класа стои зад кадифено въже и генерира две изображения в минута.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Най-страшният vendor lock-in е онзи, който започва с демо, на което не можеш да устоиш.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Градът, който го нямаше]]></title><description><![CDATA[Построих нещо, което дърпа данни от източник, изчиства ги, показва ги по-добре от оригинала. Стандартна работа. После заявих от системата…]]></description><link>https://bdteo.com/bg/the-city-that-wasnt-there/</link><guid isPermaLink="false">https://bdteo.com/bg/the-city-that-wasnt-there/</guid><pubDate>Sun, 08 Feb 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Построих нещо, което дърпа данни от източник, изчиства ги, показва ги по-добре от оригинала. Стандартна работа.&lt;/p&gt;
&lt;p&gt;После заявих от системата втория по големина запис. Всичко друго връщаше стотици резултати. Този: нула. Не счупен. Просто празен.&lt;/p&gt;
&lt;p&gt;Предположих, че аз съм объркал нещо. Проверих кода си три пъти. Тествах endpoint-а директно. Записът съществува в интерфейса им. Просто е... кух.&lt;/p&gt;
&lt;p&gt;Тогава започнах да ровя.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Източникът изглеждаше изчерпателен. Широк обхват, полиран интерфейс, чист API. Но доброволното участие означава дупки, които не можеш да видиш от документацията.&lt;/p&gt;
&lt;p&gt;Три конкурента имаха данни за същите обекти. Пълни записи. Значи информацията съществува някъде.&lt;/p&gt;
&lt;p&gt;Не липсваше endpoint. На endpoint-а му липсваше реалност.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Зададох въпрос, който никой не се беше сетил да зададе: къде са живели тези данни преди интернет?&lt;/p&gt;
&lt;p&gt;Отговорът: печатни периодични издания. Архиви. Аналогови формати, които вървят от 1800-те. Публикувани три пъти седмично. Без структурирани данни, само документи на сайт.&lt;/p&gt;
&lt;p&gt;Затова свалям един. Плътна институционална проза, обявления, заровени из поделения. Данните са там.&lt;/p&gt;
&lt;p&gt;Конкурентите ми го правят ръчно от трийсет години.&lt;/p&gt;
&lt;p&gt;Пиша scraper за един следобед. Отчасти любопитство, отчасти напук.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Парсването на документи е мястото, където нещата стават истински болезнени.&lt;/p&gt;
&lt;p&gt;Една-единствена дума се цепи от soft hyphen — Unicode U+00AD, невидим за окото, фатален за всеки regex. Взираш се в екрана и мислиш, че шаблонът ти е грешен. Не е. В текста се крие призрачен символ. JavaScript-овото &lt;code class=&quot;language-text&quot;&gt;\w&lt;/code&gt; не съвпада с non-ASCII символи, така че обикновени думи стават невъзможни за намиране. Числата съдържат фантомни интервали от renderer-а: &quot;20. 000&quot; вместо &quot;20.000.&quot;&lt;/p&gt;
&lt;p&gt;Всеки бъг отнема повече време да се намери, отколкото да се поправи. Това винаги е съотношението при извличането на текст — 90% детективска работа, 10% код.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Десет записа се материализират от шума. Дати, идентификатори, локации — всичко там, където трябва да бъде. Пускам го два пъти, за да съм сигурен, че не халюцинирам. Същият резултат. Наистина работи.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Парсването ти показва какво има. Започвам да търся какво няма. ID-тата са последователни. Изброявам ги.&lt;/p&gt;
&lt;p&gt;53% са мъртви. Системата изтрива приключените записи — без архив, без история. Някои записи съществуват, но имат нула подкрепящи документи. Отговорът: посетете ни на място. През 2026.&lt;/p&gt;
&lt;p&gt;Източникът не е база данни. Той е прозорец — и някой постоянно го затваря.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Първият източник на данни оформи архитектурата. Вторият счупи всяко предположение.&lt;/p&gt;
&lt;p&gt;Трябваше ми втора архитектура. Което е учтив начин да кажа, че първата всъщност не беше архитектура — само работещо решение, което случайно пасваше на един случай. Странният източник показва истината: строил си за данните, които си имал, не за данните, които ще срещнеш.&lt;/p&gt;
&lt;p&gt;Този път изграждам истинска. Registry pattern, споделени интерфейси, базови contracts, които позволяват на всяка имплементация да остане вярна на себе си.&lt;/p&gt;
&lt;p&gt;Архитектурата е по-добра, защото изчаках. Ако я бях построил в първия ден, щях да проектирам за единствения източник, който познавах. Вторият — странният — ме принуди да намеря кое всъщност има значение.&lt;/p&gt;
&lt;p&gt;Не можеш да проектираш за неизвестното. Но можеш да рефакторираш, когато то пристигне.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Архитектурата ме научи как да строя. Пазарът ме научи за какво да строя.&lt;/p&gt;
&lt;p&gt;Влизам на пазар с утвърден играч, който работи от трийсет години. Технологията им изглежда като 2005. Ровът им не е технология — той е доверие, разпознаваемост на марката, десетилетия натрупани данни.&lt;/p&gt;
&lt;p&gt;Модерният конкурент стартира преди три години с AI и лъскав UI. Подби цената на стария играч. Три години по-късно старият играч още доминира. Оказва се, по-евтино не значи автоматично по-добре позиционирано.&lt;/p&gt;
&lt;p&gt;Закотвянето има значение: първата цена става отправната точка. Лесно е да свалиш по-късно, почти невъзможно да вдигнеш. Абонаментът не е продуктът — той е вратата към това, което стои зад него.&lt;/p&gt;
&lt;p&gt;Поставям висока цена. Винаги мога да сляза надолу.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Четири урока, сгъстени:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Авторитетен не значи пълен.&lt;/strong&gt; Първичният източник пропускаше цял сегмент. Данните съществуваха — просто не там, където някой очакваше.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Вторият източник разкрива архитектурата ти.&lt;/strong&gt; Научаваш истината за дизайна си само когато нещо откаже формата, която си построил.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Данните не са постоянни.&lt;/strong&gt; Ако ти трябват, запази ги. Източникът няма.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Ценообразувай за това, в което се превръщаш, не за това, което си.&lt;/strong&gt; Абонаментът е врата. Построй това, което стои зад нея.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Интересната работа живее в пролуките. Там живея и аз.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Опашката, която никога не спря]]></title><description><![CDATA[Имейлите се проваляха. Тази част беше очаквана — счупени SMTP данни за достъп по време на миграция. Неочакваното беше друго: те никога не…]]></description><link>https://bdteo.com/bg/the-queue-that-never-stopped/</link><guid isPermaLink="false">https://bdteo.com/bg/the-queue-that-never-stopped/</guid><pubDate>Sat, 07 Feb 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Имейлите се проваляха. Тази част беше очаквана — счупени SMTP данни за достъп по време на миграция. Неочакваното беше друго: те никога не спряха да се провалят.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Таблото на Horizon: зелено. Работниците: здрави. Redis: расте бавно. Никакви аларми, никакви грешки в логовете. Само тихо натрупване на задачи, които опитваха, опитваха и пак опитваха.&lt;/p&gt;
&lt;p&gt;Забелязах го само защото паметта на Redis не падна обратно, след като поправих SMTP конфигурацията. Нещо още беше вътре и дъвчеше повторни опити. Хиляди.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Предположих, че опашката ще се справи. Това е сделката: една задача се проваля, пробва още няколко пъти, пада в &lt;code class=&quot;language-text&quot;&gt;failed_jobs&lt;/code&gt;. Продължаваш нататък.&lt;/p&gt;
&lt;p&gt;Освен ако задачата не е Mailable.&lt;/p&gt;
&lt;p&gt;Когато изпратиш Mailable към опашка, Laravel го обвива в задача. &lt;code class=&quot;language-text&quot;&gt;maxTries&lt;/code&gt; на тази задача идва от свойството &lt;code class=&quot;language-text&quot;&gt;$tries&lt;/code&gt; на Mailable-а. Ако не го зададеш — а защо би, документацията едва го споменава — то се сериализира като &lt;code class=&quot;language-text&quot;&gt;null&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;null&lt;/code&gt; не значи „използвай стойността по подразбиране на supervisor-а“. &lt;code class=&quot;language-text&quot;&gt;null&lt;/code&gt; значи „без лимит“. Horizon вижда &lt;code class=&quot;language-text&quot;&gt;null&lt;/code&gt; и си мисли: тази задача иска да опитва отново завинаги. И го прави.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Оказа се, че е известен бъг. &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;. Флагът &lt;code class=&quot;language-text&quot;&gt;--tries&lt;/code&gt; на supervisor-а се игнорира, когато сериализираният payload на задачата носи &lt;code class=&quot;language-text&quot;&gt;maxTries: null&lt;/code&gt;. Собствената декларация на задачата печели, а тя казва: никога не спирай.&lt;/p&gt;
&lt;p&gt;Двадесет и девет Mailable класа. Всеки един без изрично свойство &lt;code class=&quot;language-text&quot;&gt;$tries&lt;/code&gt;. Всеки един потенциално безсмъртен.&lt;/p&gt;
&lt;hr&gt;
&lt;p&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 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;Две свойства. Двадесет и девет файла. Това е.&lt;/p&gt;
&lt;p&gt;Един първоначален опит, един retry, после &lt;code class=&quot;language-text&quot;&gt;failed_jobs&lt;/code&gt;. Така, както предполагах, че винаги е работело.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Тествам го така, както би тествал капан за мишки. Чупя SMTP конфигурацията нарочно. Пускам един имейл към опашката. Гледам Horizon. Два опита. Failed job. Край. Без призраци в опашката.&lt;/p&gt;
&lt;p&gt;После поправям останалите двадесет и осем.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Три урока, сгъстени:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;&lt;code class=&quot;language-text&quot;&gt;null&lt;/code&gt; не е „default“.&lt;/strong&gt; В сериализираните payload-и на задачи &lt;code class=&quot;language-text&quot;&gt;maxTries: null&lt;/code&gt; значи без лимит. Supervisor конфигурацията ти е предложение, не правило.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Зелените dashboard-и лъжат.&lt;/strong&gt; Horizon показваше здрави worker-и, които щастливо обработваха задачи, които никога нямаше да приключат.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Framework default-ите не винаги са разумни.&lt;/strong&gt; Laravel не задава &lt;code class=&quot;language-text&quot;&gt;$tries&lt;/code&gt; на Mailables. Ти трябва да го направиш. Документацията няма да те предупреди, докато вече нямаш пожар.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Най-страшните бъгове са онези, които изглеждат като нормална работа. Този изглеждаше така — седмици наред.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Ядрената опция за масови изтривания: TRUNCATE + повторно вмъкване (MySQL/InnoDB)]]></title><description><![CDATA[Трябва да изтриеш милиони редове от MySQL таблица. Посягаш към: И после гледаш как progress bar-ът остарява в реално време. Опитваш да бъдеш…]]></description><link>https://bdteo.com/bg/todo-bulk-deletion-nuclear-option/</link><guid isPermaLink="false">https://bdteo.com/bg/todo-bulk-deletion-nuclear-option/</guid><pubDate>Sat, 13 Dec 2025 13:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Трябва да изтриеш милиони редове от MySQL таблица.&lt;/p&gt;
&lt;p&gt;Посягаш към:&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;И после гледаш как progress bar-ът остарява в реално време.&lt;/p&gt;
&lt;p&gt;Опитваш да бъдеш отговорен и го разбиваш на парчета:&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;По-добре. Все още бавно. Все още шумно. Все още скъпо.&lt;/p&gt;
&lt;p&gt;Ако изтриваш &lt;strong&gt;по-голямата част&lt;/strong&gt; от таблицата (практическо правило: &lt;strong&gt;~80%+&lt;/strong&gt;), има друг ход, груб и безмилостно ефективен:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Не изтривай това, което не искаш. &lt;strong&gt;Запази това, което искаш, и занули останалото.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Наричам го &lt;strong&gt;ядрената опция&lt;/strong&gt;: &lt;strong&gt;TRUNCATE + повторно вмъкване&lt;/strong&gt;.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;Защо &lt;code class=&quot;language-text&quot;&gt;DELETE&lt;/code&gt; остава бавен (дори на парчета)&lt;/h2&gt;
&lt;p&gt;InnoDB не „маха“ редове. Той върши работа.&lt;/p&gt;
&lt;p&gt;Много работа:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Операции ред по ред&lt;/strong&gt;: намира, заключва, маркира като изтрито.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Поддръжка на индекси&lt;/strong&gt;: всяко изтриване докосва всеки вторичен индекс.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Undo/redo logging&lt;/strong&gt;: engine-ът трябва да запази способността да върне назад и да се възстанови.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Въртене на buffer pool-а&lt;/strong&gt;: постоянно правиш страници dirty и изхвърляш полезни.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Влияние върху replication&lt;/strong&gt;: големите delete потоци са прекрасен начин да си произведеш replica lag.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Реалистична сметка на салфетка:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;27M реда при ~6,000 реда/сек ≈ &lt;strong&gt;75 минути&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Това не е бъг. Това е моделът на разхода, който си избрал.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;Ядрената опция: TRUNCATE + повторно вмъкване&lt;/h2&gt;
&lt;p&gt;Тази техника обръща модела на разхода.&lt;/p&gt;
&lt;p&gt;Вместо да плащаш за всеки изтрит ред, плащаш за всеки &lt;strong&gt;запазен&lt;/strong&gt; ред.&lt;/p&gt;
&lt;p&gt;Алгоритъм:&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) Копирай редовете, които искаш да запазиш, във временна таблица
2) TRUNCATE на оригиналната таблица (бързо)
3) Вмъкни запазените редове обратно в оригиналната таблица
4) Изтрий временната таблица&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;И да: нарича се „ядрена“ с причина. Умишлено е тъпа сила.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;Защо е бързо&lt;/h2&gt;
&lt;p&gt;Печалбите са механични:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Операция&lt;/th&gt;
&lt;th align=&quot;right&quot;&gt;Приблизителна цена&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;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;изтрива и пресъздава таблицата (на metadata ниво)&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;sequential scan + bulk write за запазените редове&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; без „delete tax“&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Няма overhead от изтриване ред по ред. Няма обновяване на индекси за премахнатите редове (защото те изчезват наведнъж).&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;Кога да го използваш (и кога не)&lt;/h2&gt;
&lt;h3&gt;Използвай го, когато&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Изтриваш &lt;strong&gt;по-голямата част&lt;/strong&gt; от таблицата (пак: &lt;strong&gt;~80%+&lt;/strong&gt; е границата, където това започва да блести).&lt;/li&gt;
&lt;li&gt;Можеш чисто да дефинираш „редовете за запазване“.&lt;/li&gt;
&lt;li&gt;Можеш да си позволиш кратка недостъпност / maintenance window.&lt;/li&gt;
&lt;li&gt;Таблицата не е активно реферирана от foreign keys от други таблици (или можеш безопасно да управляваш constraints).&lt;/li&gt;
&lt;li&gt;Имаш &lt;strong&gt;достатъчно диск&lt;/strong&gt; за временната таблица.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Не го използвай, когато&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Трябва ти &lt;strong&gt;нулев downtime&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Таблицата е силно реферирана от foreign keys, които не можеш да пипнеш.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Трябва&lt;/em&gt; да задействаш DELETE triggers.&lt;/li&gt;
&lt;li&gt;Изтриваш само малка част от редовете (chunked delete може да е по-простата победа).&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2&gt;Практическо правило за решение&lt;/h2&gt;
&lt;p&gt;Ако искаш едно изречение, което можеш да кажеш на review:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Ако delete-ът ще махне по-голямата част от таблицата, спри да триеш. Запази и изгради наново.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Или, ако предпочиташ ASCII:&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;Колко изтриваш?

&amp;lt; 50%     -&gt; chunked DELETE (и филтри, съобразени с индексите)
50–80%    -&gt; измери и двата подхода
&gt; 80%     -&gt; TRUNCATE + reinsert (ако constraints позволяват)&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;hr&gt;
&lt;h2&gt;Имплементация (SQL)&lt;/h2&gt;
&lt;p&gt;Ето минималната форма:&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;Две бележки, които имат значение в production:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;TRUNCATE&lt;/code&gt; е DDL в MySQL. Той &lt;strong&gt;implicit commit-ва&lt;/strong&gt; и не можеш да го върнеш назад като нормална transaction.&lt;/li&gt;
&lt;li&gt;Искаш maintenance window и backup. Това не е „пусни го live и да видим“.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2&gt;Имплементация (Laravel/PHP)&lt;/h2&gt;
&lt;p&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 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;Щипка rubber-duck енергия: прочети функцията пак и попитай бъдещото си аз —&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;„Сигурен ли съм, че тази таблица може да бъде празна за момент?“&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Ако отговорът не е недвусмислено да, това не е инструментът.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;Капани, с които трябва да се съобразиш&lt;/h2&gt;
&lt;h3&gt;Auto-increment се reset-ва&lt;/h3&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;TRUNCATE&lt;/code&gt; reset-ва &lt;code class=&quot;language-text&quot;&gt;AUTO_INCREMENT&lt;/code&gt;. Ако трябва да го запазиш:&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;Ако други таблици реферират тази, &lt;code class=&quot;language-text&quot;&gt;TRUNCATE&lt;/code&gt; може да е забранен или unsafe. Не „просто спирай checks“ и не се надявай.&lt;/p&gt;
&lt;h3&gt;Triggers&lt;/h3&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;TRUNCATE&lt;/code&gt; &lt;strong&gt;не&lt;/strong&gt; задейства DELETE triggers. Ако ти трябват side effects от trigger-и, връщаш се към &lt;code class=&quot;language-text&quot;&gt;DELETE&lt;/code&gt;.&lt;/p&gt;
&lt;h3&gt;Дисково пространство&lt;/h3&gt;
&lt;p&gt;Трябва ти място за запазения dataset (временната таблица). Провери първо:&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;Replication / binlog&lt;/h3&gt;
&lt;p&gt;Това е DDL + bulk insert. Пак може да причини replica lag. Прави го умишлено, monitor-вай lag-а и не се преструвай, че е безплатно.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;Ако ти трябва (почти) нулев downtime&lt;/h2&gt;
&lt;p&gt;Този текст е за бързия чук.&lt;/p&gt;
&lt;p&gt;Ако ти трябва скалпел, използвай инструментите, създадени за това:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;pt-archiver&lt;/code&gt; (Percona Toolkit) за batched deletes с pacing, щадящ репликите&lt;/li&gt;
&lt;li&gt;partitioning стратегии (drop partitions вместо редове)&lt;/li&gt;
&lt;li&gt;shadow-table подходи + контролиран swap (по-сложно, повече движещи се части)&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2&gt;Финална мисъл&lt;/h2&gt;
&lt;p&gt;Това не е хитър трик. Това е избор за коя работа плащаш.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Type 0 рефакторинг: стъпката преди първа стъпка]]></title><description><![CDATA[Има един вид рефакторинг, който екипите правят постоянно, печелят от него веднага и почти никога не го назовават. Това е работата, която…]]></description><link>https://bdteo.com/bg/type-0-refactoring-step-before-step-one/</link><guid isPermaLink="false">https://bdteo.com/bg/type-0-refactoring-step-before-step-one/</guid><pubDate>Sat, 13 Dec 2025 12:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Има един вид рефакторинг, който екипите правят постоянно, печелят от него веднага и почти никога не го назовават.&lt;/p&gt;
&lt;p&gt;Това е работата, която вършиш точно преди да докоснеш страшния файл. Feature request-ът те принуждава да влезеш в разхвърляния модул. Инцидентът идва, а bug-ът се крие някъде в метод, който изглежда все едно има собствена метеорологична система.&lt;/p&gt;
&lt;p&gt;Не препроектираш системата. Не въвеждаш нова абстракция. Не „подобряваш“ нищо по хитър начин.&lt;/p&gt;
&lt;p&gt;Просто правиш кода достатъчно четим, за да можеш да работиш.&lt;/p&gt;
&lt;p&gt;Започнах да наричам това &lt;strong&gt;Type 0 рефакторинг&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Type 0 рефакторингът&lt;/strong&gt; е подготвително, &lt;strong&gt;запазващо поведението почистване&lt;/strong&gt;, което прави кода по-лесен за разбиране &lt;strong&gt;преди&lt;/strong&gt; архитектурни рефакторинги, performance работа или feature работа.&lt;/p&gt;
&lt;p&gt;Това е стъпката „подсуши пода, преди да ремонтираш кухнята“. Повечето екипи вече я правят неформално. Когато я назовеш, тя се превръща в общ инструмент.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;Истинската причина Type 0 да съществува: хората имат лимит на работната памет&lt;/h2&gt;
&lt;p&gt;Ето грубата истина зад идеята:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Моят мозък (и твоят) не е създаден да debug-ва надеждно метод от 2000 реда под напрежение.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Това не е личен дефект. Просто така работи мисленето.&lt;/p&gt;
&lt;p&gt;Debugging-ът те кара да държиш едновременно:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;текущия execution path&lt;/li&gt;
&lt;li&gt;релевантното състояние&lt;/li&gt;
&lt;li&gt;какво всъщност означава всяка променлива&lt;/li&gt;
&lt;li&gt;набора от възможни разклонения&lt;/li&gt;
&lt;li&gt;последствията от „ако стане това, тогава...“&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;В малък код това е управляемо.&lt;/p&gt;
&lt;p&gt;В голям код с висока cyclomatic complexity се превръща във вероятностно налучкване. Пак можеш да имаш късмет, но е скъпо и рисковано, особено по време на hotfix.&lt;/p&gt;
&lt;p&gt;Type 0 е практичен отговор: това е начинът &lt;strong&gt;бързо да си купиш яснота&lt;/strong&gt;, без да поемаш цената и риска на „истински рефакторинг“.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;Защо се казва „Type 0“&lt;/h2&gt;
&lt;p&gt;Името не дойде от голяма теория. Дойде от момент с високо напрежение.&lt;/p&gt;
&lt;p&gt;Работех по hotfix. Bug-ът беше заровен в метод, който на практика беше собствена малка вселена — &lt;strong&gt;около 2000 реда&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Bug-ът не беше концептуално труден. Методът беше.&lt;/p&gt;
&lt;p&gt;Всяко „какво става, ако...“ се разклоняваше в още десет въпроса, а разклоняването не беше от полезния вид. Беше incidental complexity: шум, повторение, неясни имена и структура, която не съвпадаше с менталния модел, нужен за debugging.&lt;/p&gt;
&lt;p&gt;Това, от което имах нужда, не беше съвършенство. Имах нужда от &lt;strong&gt;debuggability&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;по-малко разклонения на екран&lt;/li&gt;
&lt;li&gt;по-ясни „стъпки“ с имена&lt;/li&gt;
&lt;li&gt;по-малко шум&lt;/li&gt;
&lt;li&gt;по-малко време да препарсвам току-що прочетеното&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Но напрежението във времето не позволяваше по-голям рефакторинг или „идиоматичен redesign“. Да го направя отговорно щеше да е половин ден (или повече), включително ръчно тестване. В hotfix прозорец това не е дисциплина; това е хазарт.&lt;/p&gt;
&lt;p&gt;Затова помолих един LLM да предложи възможности за refactoring за класа и този метод, без да му казвам защо.&lt;/p&gt;
&lt;p&gt;Той се върна със списък от четири „типа“ рефакторинг. Всички разумни. Всички приложими. Всички твърде скъпи за онзи момент.&lt;/p&gt;
&lt;p&gt;После зададе учтивия въпрос:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;“Should I start with Type 1?”&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Тогава отговорих:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;“No. Let’s start with Type 0.”&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;И дефинирах Type 0 на място: ограничен, механичен набор от промени, които намаляват complexity и увеличават четимостта &lt;strong&gt;без да променят поведението или архитектурата&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Методът стана навигируем. Мозъкът ми отново можеше да следи изпълнението. Намерих bug-а, поправих го и го ship-нах без collateral damage.&lt;/p&gt;
&lt;p&gt;Затова харесвам името &lt;strong&gt;Type 0&lt;/strong&gt;: това е рефакторингът, който правиш &lt;strong&gt;преди&lt;/strong&gt; „истинските“ типове рефакторинг, особено когато си под напрежение и ти трябва безопасен начин бързо да създадеш яснота.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;Проблемът, който Type 0 решава&lt;/h2&gt;
&lt;p&gt;Повечето съвети за refactoring приемат, че вече можеш да &lt;em&gt;видиш&lt;/em&gt; дизайна.&lt;/p&gt;
&lt;p&gt;В реални codebase-и:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;методите са дълги и многоцелеви&lt;/li&gt;
&lt;li&gt;повтарящи се изрази и incidental complexity крият намерението&lt;/li&gt;
&lt;li&gt;променливите са криптични (&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;мъртъв код и unused imports създават ментален шум&lt;/li&gt;
&lt;li&gt;„формата“ на кода е толкова разхвърляна, че дори малките промени изглеждат рискови&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Когато опиташ „истински refactoring“ върху това (граници, patterns, местене на отговорности), трупаш несигурност върху несигурност:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;не можеш лесно да кажеш кое поведение запазваш&lt;/li&gt;
&lt;li&gt;не можеш да предвидиш blast radius-а&lt;/li&gt;
&lt;li&gt;review-тата се разпадат в субективни спорове&lt;/li&gt;
&lt;li&gt;хората започват да се страхуват да докосват нещата и бъркотията се натрупва&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Type 0 е начинът първо да свалиш когнитивния товар.&lt;/strong&gt; Той създава стабилна основа, върху която по-дълбоката работа може да се случи безопасно.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;Посягай към Type 0, когато...&lt;/h2&gt;
&lt;p&gt;Type 0 е най-ценен, когато:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;трябва да debug-ваш бързо (hotfix-и, инциденти), а кодът е твърде голям или разклонен, за да мислиш за него безопасно&lt;/li&gt;
&lt;li&gt;усещаш, че си „изгубен в метода“ и препрочиташ една и съща секция, защото структурата не помага на работната ти памет&lt;/li&gt;
&lt;li&gt;кодът е правилен, но нечетим, и не можеш да си позволиш да „почистваш логиката“, а само да я покажеш&lt;/li&gt;
&lt;li&gt;искаш да намалиш риска преди по-дълбока работа (знаеш, че по-късно ще рефакторираш, но първо ти трябва ясна карта на текущото поведение)&lt;/li&gt;
&lt;li&gt;искаш да превърнеш tribal knowledge в четима структура, така че debugging-ът да не зависи от един човек&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Type 0 не е лукс. В тези случаи често е най-бързият начин да си върнеш контрола.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;Дефиниция, която можеш да използваш в екипа си&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Type 0 refactoring е набор от микро-рефакторинги, които подобряват четимостта и maintainability, без да променят поведението или архитектурата.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Той е умишлено ограничен. Ограниченията са feature-ът.&lt;/p&gt;
&lt;p&gt;Type 0 се състои от четири задължителни sub-pattern-а:&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;И следва три твърди правила:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Без промени в поведението&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Без архитектурни промени&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Без „хитри“ подобрения извън четирите pattern-а&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Ако нарушиш тези правила, вече не правиш Type 0 — преминал си в друга категория работа, а тя изисква друга координация, друга строгост в review-то и често друга стратегия за тестване.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;Защо изобщо да го назоваваме?&lt;/h2&gt;
&lt;p&gt;Защото назоваването променя начина, по който екипите се координират.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;„В този PR правя само Type 0“ казва на reviewer-ите какво да гледат: запазване на поведението и четимост, не архитектурни дебати.&lt;/li&gt;
&lt;li&gt;„Трябва ни Type 0, преди да рефакторираме това“ е честно признание, че кодът още не е готов за по-дълбока промяна.&lt;/li&gt;
&lt;li&gt;„Нека направим Type 0 като Step 0“ създава малък ритуал, който те предпазва да строиш върху хаос.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2&gt;Четирите sub-pattern-а&lt;/h2&gt;
&lt;h3&gt;0a. Method extraction (основата)&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Цел:&lt;/strong&gt; разбий големите методи на малки, фокусирани методи, така че човек да може да чете намерението линейно.&lt;/p&gt;
&lt;p&gt;Правила на палеца:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;разбивай методи, които са твърде дълги, за да ги държиш в работната памет&lt;/li&gt;
&lt;li&gt;всеки extracted method трябва да прави едно нещо и да има описателно име&lt;/li&gt;
&lt;li&gt;извличай смислени стъпки, не произволни парчета от N реда&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Защо работи (особено при debugging):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;по-малките методи създават етикети за execution path-а&lt;/li&gt;
&lt;li&gt;scroll от 2000 реда става кратък orchestration method, през който можеш да минеш наум&lt;/li&gt;
&lt;li&gt;можеш да сложиш breakpoints на семантични граници („validate input“, „build query“, „apply filters“), вместо да ловуваш&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;0b. Conciseness (намаляване на incidental complexity)&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Цел:&lt;/strong&gt; махни визуалния шум, за да изпъкне намерението.&lt;/p&gt;
&lt;p&gt;Примери:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;извади повтарящи се изрази в локални променливи&lt;/li&gt;
&lt;li&gt;извади повтарящи се log contexts / key strings / URL fragments в променливи&lt;/li&gt;
&lt;li&gt;предпочитай езикови features, които предават намерението директно&lt;/li&gt;
&lt;li&gt;опрости прекалено бъбривата interpolation&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Защо работи:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;намалява когнитивния товар&lt;/li&gt;
&lt;li&gt;прави diff-овете по-малки и промените по-безопасни&lt;/li&gt;
&lt;li&gt;предотвратява copy/paste drift&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;0c. Empathy (чиста четимост)&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Цел:&lt;/strong&gt; пиши за следващия човек, не за compiler-а.&lt;/p&gt;
&lt;p&gt;Empathy означава:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;използвай описателни имена на променливи (избягвай &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;, освен ако наистина са очевидни)&lt;/li&gt;
&lt;li&gt;поддържай последователна терминология в модула&lt;/li&gt;
&lt;li&gt;преименувай подвеждащи имена&lt;/li&gt;
&lt;li&gt;прави кода self-documenting&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Litmus test:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Ако някой чете това в 2 сутринта по време на инцидент, ще му помогне ли да задържи execution path-а в главата си?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;0d. Dead code removal (махане на лъжите)&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Цел:&lt;/strong&gt; изтрий всичко, което се преструва, че има значение, но няма.&lt;/p&gt;
&lt;p&gt;Примери:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;unused private methods&lt;/li&gt;
&lt;li&gt;unused imports&lt;/li&gt;
&lt;li&gt;commented-out стари подходи&lt;/li&gt;
&lt;li&gt;deprecated helpers, които никой не вика&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Защо работи:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;по-малко код означава по-малко неща за погрешно разбиране&lt;/li&gt;
&lt;li&gt;резултатите от search стават надеждни&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2&gt;Какво Type 0 не е&lt;/h2&gt;
&lt;p&gt;Type 0 не е:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;промяна на service boundaries&lt;/li&gt;
&lt;li&gt;въвеждане на нови абстракции или patterns&lt;/li&gt;
&lt;li&gt;re-architecting на workflow&lt;/li&gt;
&lt;li&gt;подмяна на библиотеки&lt;/li&gt;
&lt;li&gt;пренареждане на отговорности между layers&lt;/li&gt;
&lt;li&gt;„поправяне“ на логика, която подозираш, че е грешна (освен ако изрично не обявиш промяна в поведението и не я тестваш)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Ако се хванеш да казваш:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;„Докато съм тук, нека също...“&lt;/li&gt;
&lt;li&gt;„Това би било по-хубаво, ако...“&lt;/li&gt;
&lt;li&gt;„Вероятно трябва да redesign-нем...“&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Може би напускаш Type 0. Това не е лошо само по себе си, но трябва да е умишлено.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;Основното обещание: запазване на поведението (и как да остане вярно)&lt;/h2&gt;
&lt;p&gt;Type 0 работи само ако екипите вярват на обещанието.&lt;/p&gt;
&lt;p&gt;И да, прав си да си подозрителен: &lt;strong&gt;method extraction може случайно да промени поведение&lt;/strong&gt; (early returns, variable scope, evaluation order, exception behavior).&lt;/p&gt;
&lt;p&gt;Затова Type 0 има нужда от дисциплина, която го държи честен:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Extract as-is, then rename/cleanup.&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;First pass: премести код в методи, без да променяш логиката&lt;/li&gt;
&lt;li&gt;Second pass: приложи conciseness + empathy&lt;/li&gt;
&lt;li&gt;Third pass: махни dead code&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Практични guardrails:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;не пренареждай condition checks „за четимост“&lt;/li&gt;
&lt;li&gt;не заменяй логика с „еквивалентна“ логика, освен ако вече си извън Type 0&lt;/li&gt;
&lt;li&gt;внимавай с променливи, които преди са били в shared scope&lt;/li&gt;
&lt;li&gt;третирай „малките“ control-flow разлики като реални разлики&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;И ако имаш &lt;em&gt;каквато и да е&lt;/em&gt; safety net, дори тънка:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;пусни фокусиран test&lt;/li&gt;
&lt;li&gt;replay-ни failing scenario-то&lt;/li&gt;
&lt;li&gt;валидирай единствения path, който докосваш&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Type 0 е за това да си бърз — &lt;strong&gt;но бърз чрез намаляване на cognitive complexity&lt;/strong&gt;, не бърз чрез прескачане на safety.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;Type 0 като повтаряем екипен ритуал&lt;/h2&gt;
&lt;h3&gt;1) Реши обхвата (timebox помага)&lt;/h3&gt;
&lt;p&gt;Примери:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;„Type 0 the hot path before debugging.“&lt;/li&gt;
&lt;li&gt;„Type 0 only the path touched by this bug fix.“&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;2) Идентифицирай „гръбнака“ на кода&lt;/h3&gt;
&lt;p&gt;Намери entry method(s) и branching points. Превърни този гръбнак в четим разказ чрез extraction.&lt;/p&gt;
&lt;h3&gt;3) Приложи четирите sub-pattern-а по ред&lt;/h3&gt;
&lt;p&gt;Method extraction → conciseness → empathy → dead code removal.&lt;/p&gt;
&lt;h3&gt;4) Дръж „Type 0 checklist“ в 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; No behavior changes (inputs/outputs unchanged)&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled&gt; No architectural moves&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled&gt; Methods extracted and named as meaningful steps&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled&gt; Repeated expressions extracted where it improves clarity&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled&gt; Variables renamed; terminology consistent&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled&gt; Dead code and unused imports removed&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2&gt;Финална мисъл&lt;/h2&gt;
&lt;p&gt;Type 0 refactoring е най-простото обещание, което един developer може да даде:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;„Оставям този код по-лесен за работа, отколкото го намерих — без да променям какво прави.“&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Понякога е “nice to have”.&lt;/p&gt;
&lt;p&gt;А понякога е единственият начин човек безопасно да се движи бързо в mess с висока complexity, особено по време на hotfix.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Измий още една чиния: просто правило за постоянно чиста codebase]]></title><description><![CDATA[TL;DR: Отнасяй се към всяка промяна в codebase-а си като към готвене. Ще изцапаш няколко чинии. Когато приключиш, измий не само чиниите…]]></description><link>https://bdteo.com/bg/wash-one-more-plate-refactoring-philosophy/</link><guid isPermaLink="false">https://bdteo.com/bg/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;: Отнасяй се към всяка промяна в codebase-а си като към готвене. Ще изцапаш няколко чинии. Когато приключиш, измий не само чиниите, които си използвал - измий &lt;em&gt;още една&lt;/em&gt;. С времето този малък излишък от грижа се натрупва в кухня (codebase), която остава чиста, вместо да се разпада в хаос.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2&gt;Метафората: готвене, чинии и код&lt;/h2&gt;
&lt;p&gt;Представи си професионална кухня. Всяко приготвено ястие цапа няколко чинии - дори в най-подредената бригада. Сега си представи, че след като завърши ястието си, всеки готвач мие &lt;em&gt;точно&lt;/em&gt; чиниите, които е изцапал. Кухнята ще се държи на ръба на приемливата чистота, но ентропията ще пропълзи вътре: малко засъхнала мръсотия тук, оцветена дъска за рязане там. Накрая бъркотията се натрупва.&lt;/p&gt;
&lt;p&gt;Сега обърни правилото: след готвене всеки chef мие &lt;strong&gt;една чиния повече, отколкото е изцапал&lt;/strong&gt;. Бавно кухнята става по-чиста от преди - не просто поддържана, а подобрена. Същото важи и за софтуера: всяка задача, която поемаш, трябва да добавя поне малък излишък от чистота към codebase-а - още един тест, по-ясно име, една разделена функция, премахната мъртва dependency. Навикът за &quot;+1 чиния&quot; е начинът една codebase да &lt;em&gt;остава&lt;/em&gt; здрава.&lt;/p&gt;
&lt;p&gt;Наричам това &lt;strong&gt;правилото &quot;Измий още една чиния&quot;&lt;/strong&gt;.&lt;/p&gt;
&lt;h2&gt;Отзвук от занаята: в добра компания си&lt;/h2&gt;
&lt;p&gt;Това не е самотна философия. Мислители в софтуера проповядват сходни идеи от десетилетия:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&quot;Винаги оставяй лагера по-чист, отколкото си го намерил.&quot;&lt;/strong&gt; Това е класическото &lt;a href=&quot;https://deviq.com/principles/boy-scout-rule/&quot;&gt;Boy Scout Rule&lt;/a&gt;, популяризирано в софтуера от Robert C. Martin. Духът е същият: подобрявай по малко, всеки път.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Technical debt като метафора&lt;/strong&gt; (Ward Cunningham): дългът трупа лихва - игнорирай го и утре &quot;кухнята&quot; струва повече за използване. Ако изплащаш част от него в движение, оставаш платежоспособен.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Refactoring като малки, постоянни стъпки&lt;/strong&gt; (Martin Fowler): миниатюрни промени, които запазват поведението, но подобряват дизайна. Малките стъпки означават нисък риск и устойчив momentum.&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): първо correctness, после чистота, после performance. Измиването на допълнителната чиния живее във фазата &quot;make it right&quot; - преди да оптимизираш преждевременно.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Broken windows theory, приложена към кода&lt;/strong&gt; (Andrew Hunt &amp;#x26; David Thomas): видимата бъркотия кани още бъркотия. Поправянето на един &quot;прозорец&quot;, преди да се разпространи, пази квартала (codebase-а).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Тези идеи се подсилват взаимно. Всички казват едно и също: &lt;em&gt;не предавай бъркотията нататък; отдели миг да я направиш по-добра.&lt;/em&gt;&lt;/p&gt;
&lt;h2&gt;Защо допълнителната чиния има значение (дори когато си зает)&lt;/h2&gt;
&lt;h3&gt;1. &lt;strong&gt;Ентропията е реална&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Оставен без грижа, кодът не стои неутрален. Имената се разместват, pattern-ите се разпадат, абстракциите гният. Ентропията е сила; единствената насрещна сила е постоянното, постепенно подреждане. Твоята +1 чиния е micro-обръщане на ентропията.&lt;/p&gt;
&lt;h3&gt;2. &lt;strong&gt;Дългът се натрупва по-бързо, отколкото мислиш&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Цената на промяната расте с всяко &quot;ще го оправим по-късно&quot;. &quot;По-късно&quot; рядко идва. Лихвените плащания се проявяват като забавена feature работа, крехки deploy-и и test suite-ове, на които никой не вярва. Измиването на една допълнителна чиния &lt;em&gt;днес&lt;/em&gt; сваля лихвата утре.&lt;/p&gt;
&lt;h3&gt;3. &lt;strong&gt;Социалният сигнал&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Когато съотборниците видят, че почистваш след себе си (и още малко), нормата се измества. Става достоверно - и очаквано - да оставяш кода по-добър, отколкото си го намерил. Културата следва поведението.&lt;/p&gt;
&lt;h3&gt;4. &lt;strong&gt;Momentum, не перфекционизъм&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Това не е оправдание за yak shaving. Не преизграждаш кухнята по време на service. Просто минаваш с гъбата по още една чиния - малко, безопасно и бързо. Това е ключът delivery-то да остане на релси.&lt;/p&gt;
&lt;h2&gt;Как да практикуваш правилото &quot;Измий още една чиния&quot;&lt;/h2&gt;
&lt;p&gt;Ето как да вградиш навика, без да derail-неш scope-а или сроковете.&lt;/p&gt;
&lt;h3&gt;1. Приеми &quot;Micro-Refactoring&quot; като част от Definition of Done&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Преименувай объркваща променлива.&lt;/li&gt;
&lt;li&gt;Извади малка функция, за да намалиш cyclomatic complexity.&lt;/li&gt;
&lt;li&gt;Изтрий мъртъв код или неизползвани imports.&lt;/li&gt;
&lt;li&gt;Добави липсващ test за bug, който току-що си поправил.&lt;/li&gt;
&lt;li&gt;Обнови документация или README секция, която те е стреснала за минута.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Критерият: &lt;strong&gt;Ако отнема повече от няколко минути, не е чиния - това е цялата съдомиялна. Отложи го.&lt;/strong&gt; Запиши го като ticket.&lt;/p&gt;
&lt;h3&gt;2. Използвай Pull Requests като trigger за чистене&lt;/h3&gt;
&lt;p&gt;Всеки PR може да остави лагера по-чист:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Изисквай checkbox или кратка бележка &quot;Какво почисти?&quot;.&lt;/li&gt;
&lt;li&gt;Насърчавай reviewers да &lt;em&gt;искат&lt;/em&gt; малки подреждания заедно с review-то си.&lt;/li&gt;
&lt;li&gt;Отбелязвай PR-ите, които включват този допълнителен polish (shout-out в standup върши чудеса).&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;3. Автоматизирай лесните чинии&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Pre-commit hooks за formatting и linting.&lt;/li&gt;
&lt;li&gt;Static analysis, която маркира complex methods или дълги parameter lists.&lt;/li&gt;
&lt;li&gt;Dependency checkers за остарели библиотеки.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Остави автоматизираните метли да метат тривиалната бъркотия, за да могат хората да се фокусират върху logic и design.&lt;/p&gt;
&lt;h3&gt;4. Вгради го в екипните норми&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Добави правилото към working agreement-а или engineering handbook-а на екипа.&lt;/li&gt;
&lt;li&gt;Следи micro-refactor wins в retros, ако искаш измеримо доказателство.&lt;/li&gt;
&lt;li&gt;Понякога pair или mob program, за да разпространиш навика (и смелостта).&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;5. Знай кога &lt;strong&gt;да не&lt;/strong&gt; миеш&lt;/h3&gt;
&lt;p&gt;Понякога кухнята гори: production е down, или demo-то е след няколко часа. В emergency, ако трябва, разбий купчината мръсни чинии. Но се върни след кризата. Правилото не е догма; то е дисциплина.&lt;/p&gt;
&lt;h2&gt;Границата: една чиния, не мивката&lt;/h2&gt;
&lt;p&gt;Scope creep често се маскира като craftsmanship. Твоята работа е да спреш на &quot;още една чиния&quot;. Ако този малък refactor разкрие по-дълбока миризма, запиши я и продължи. Паркирай по-дълбоката поправка:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Създай ticket с label &lt;code class=&quot;language-text&quot;&gt;refactor:&lt;/code&gt; или &lt;code class=&quot;language-text&quot;&gt;techdebt:&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Свържи го с релевантния code, tests или module.&lt;/li&gt;
&lt;li&gt;Добави кратка бележка защо има значение.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Направил си дължимото: видял си бъркотията, измил си чиния и си оставил инструкции за останалото.&lt;/p&gt;
&lt;h2&gt;Пример: да превърнеш messy function във функция, която можеш да тестваш&lt;/h2&gt;
&lt;p&gt;Before:&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;Измита чиния:&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;Сега main function-а ти вика &lt;code class=&quot;language-text&quot;&gt;vatFor()&lt;/code&gt;, вместо да inline-ва logic. Добавил си micro-test за &lt;code class=&quot;language-text&quot;&gt;vatFor()&lt;/code&gt;. Това е една допълнителна чиния - просто, ограничено, полезно.&lt;/p&gt;
&lt;h2&gt;Последни мисли&lt;/h2&gt;
&lt;p&gt;Още една чиния е нещо малко. Точно това е смисълът. Не ти трябват героични refactor-и, за да държиш codebase здрава; трябва ти култура на малка, постоянна грижа. Превърни го в навик, изплети го в процеса си и след година ще се чудиш защо кухнята ти &lt;em&gt;не е&lt;/em&gt; бедствие - защото никога не си я оставил да стане такава.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;strong&gt;Call to Action&lt;/strong&gt;: Следващия път, когато докоснеш файл, попитай се: &lt;em&gt;&quot;Коя допълнителна чиния мога да измия, преди да commit-на тази промяна?&quot;&lt;/em&gt; После го направи. Повтори. Промени културата, една безупречна чиния наведнъж.&lt;/p&gt;
&lt;h3&gt;Източници и допълнително четене&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; от &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; оригиналното обяснение на Cunningham за &lt;a href=&quot;https://martinfowler.com/bliki/TechnicalDebt.html&quot;&gt;Technical Debt&lt;/a&gt; на сайта на Martin Fowler.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Martin Fowler - Continuous Micro-Refactoring:&lt;/strong&gt; книгата на Fowler, &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; обяснение на мантрата от &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; концепцията е разгледана подробно в книгата им &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;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Software Entropy &amp;#x26; Maintenance:&lt;/strong&gt; добра статия по темата е &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: обиколка на идващите възможности]]></title><description><![CDATA[TL;DR стълбица на хайпа (моето игриво класиране) Pipe Operator () – четими, линейни трансформации. Блаженство. Магнит за refactor-и…]]></description><link>https://bdteo.com/bg/php-8-5-new-features-pipe-operator-guide/</link><guid isPermaLink="false">https://bdteo.com/bg/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 стълбица на хайпа (моето игриво класиране)&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; – четими, линейни трансформации. Блаженство. &lt;em&gt;Магнит за refactor-и.&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code class=&quot;language-text&quot;&gt;#[\NoDiscard]&lt;/code&gt; attribute&lt;/strong&gt; – превръща „забравихме да използваме върнатата стойност“ в &lt;em&gt;моментално&lt;/em&gt; предупреждение. Прекрасно си пасва с pipe-овете.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Static Closures / First-Class Callables in Constant Expressions&lt;/strong&gt; – compile-time strategy maps и attribute arguments. Бонбон за framework-и.&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; – мигновен diff на environment drift. Спестява ровене из конфигурации.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Attributes on Global &amp;#x26; Class Constants&lt;/strong&gt; – metadata навсякъде: flags, deprecations, semantic 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; – очевидни, показват намерение, не мутират. Сбогом, странични ефекти от &lt;code class=&quot;language-text&quot;&gt;reset()&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code class=&quot;language-text&quot;&gt;get_exception_handler()&lt;/code&gt; и компания&lt;/strong&gt; – introspection за layered error handling. Победа на framework / infra ниво.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Intl благинки (&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; – по-гладък, локализиран UX с почти никакъв код.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Grapheme-aware Levenshtein&lt;/strong&gt; – user-facing fuzzy matching, който наистина уважава човешките символи.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Directory Object + cURL / Build Introspection / Misc&lt;/strong&gt; – полиране за консистентност и operability.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;(Да, &lt;em&gt;твоят&lt;/em&gt; ред може да е друг. Това му е забавното — спорете на кафе.)&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;1. Pipe Operator (&lt;code class=&quot;language-text&quot;&gt;|&gt;&lt;/code&gt;) – “Easy Peasy Lemon Squeezy”&lt;/h2&gt;
&lt;p&gt;Вложени извиквания и временни променливи за изхвърляне? Няма ги. Pipe operator-ът взема стойността отляво и я подава като &lt;em&gt;първи аргумент&lt;/em&gt; на callable-а отдясно. Четеш отгоре надолу, логиката тече като проза, а намерението те плясва през лицето.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Преди (подскачане през променливи):&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;Преди (гнездо от скоби):&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;След това (pipe дзен):&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;Защо има значение:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;Визуален поток на данните.&lt;/em&gt; Няма умствен стек от вложени return-и.&lt;/li&gt;
&lt;li&gt;Комбинира се прекрасно с малки pure helper-и.&lt;/li&gt;
&lt;li&gt;Насърчава разбиването на трансформациите в именувани функции / closures.&lt;/li&gt;
&lt;li&gt;Автоматично удовлетворява &lt;code class=&quot;language-text&quot;&gt;#[\NoDiscard]&lt;/code&gt;, защото стойността продължава да се движи.&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Style Tip:&lt;/strong&gt; Дръж всяка стъпка без side effects; запази &lt;em&gt;последния&lt;/em&gt; pipe за ефект (например persist, send, emit), за да виждаш с един поглед къде свършва „чистотата“.&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; – намерение, превърнато в оръжие&lt;/h2&gt;
&lt;p&gt;Колко фини бъгове са били просто „извикахме нещото, но забравихме да използваме какво връща“? Маркираш функция или метод с &lt;code class=&quot;language-text&quot;&gt;#[\NoDiscard]&lt;/code&gt;, за да изискаш резултатът му да бъде &lt;em&gt;използван&lt;/em&gt; — или съзнателно игнориран чрез &lt;code class=&quot;language-text&quot;&gt;(void)&lt;/code&gt; cast.&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 objects (&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 builders (връщат нов instance при всяко извикване).&lt;/li&gt;
&lt;li&gt;Security / side-effect gating (tokens, signatures).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Synergy:&lt;/strong&gt; В pipeline return-ът на всяка стъпка по дефиниция се консумира от следващата, така че случайните discard-и изчезват.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;3. Static Closures in Constant Expressions – &lt;em&gt;“Wait… what?!”&lt;/em&gt;&lt;/h2&gt;
&lt;p&gt;Вече можеш да вграждаш &lt;strong&gt;static&lt;/strong&gt; closures (или first-class callables) в constant expressions, default property values, attribute arguments и default parameter arrays. Мисли за compile-time registries без boot-time wiring акробатика.&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;Защо удря добре:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Премахва service-locator lookup-и за прости strategies.&lt;/li&gt;
&lt;li&gt;Бута pure mapping tables в constants (immutable + cacheable).&lt;/li&gt;
&lt;li&gt;Attributes вече могат &lt;em&gt;директно&lt;/em&gt; да капсулират логика — не само scalar metadata.&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Ограничение:&lt;/strong&gt; Трябва да е &lt;code class=&quot;language-text&quot;&gt;static&lt;/code&gt;; няма &lt;code class=&quot;language-text&quot;&gt;$this&lt;/code&gt;, няма variable capture. Ако ти трябва context, подай го експлицитно по-късно.&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; – рентген за config drift&lt;/h2&gt;
&lt;p&gt;Омръзнало ли ти е от &lt;em&gt;„ама на staging работи“&lt;/em&gt;? Този CLI flag отпечатва само INI директивите, които се различават от default-а.&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 за налагане на консистентен baseline.&lt;/li&gt;
&lt;li&gt;Бърз sanity check, когато worker се държи странно.&lt;/li&gt;
&lt;li&gt;Triage на memory/time аномалии.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Pro tip: Запиши output-а във version control като runtime baseline.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;5. Attributes on Global &amp;#x26; Class Constants – metadata навсякъде&lt;/h2&gt;
&lt;p&gt;Constants порастват от „тъпа стойност“ до „анотиран участник“. Украси domain flags, feature toggles, deprecation notices, unit semantics — &lt;em&gt;директно на мястото на дефиницията.&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 leverage:&lt;/strong&gt; Автоматично откриване на deprecations, захранване на feature catalogs, генериране на docs или налагане на policy чрез reflection.&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; – очевидното най-сетне съществува&lt;/h2&gt;
&lt;p&gt;Спри с pointer акробатиката (&lt;code class=&quot;language-text&quot;&gt;reset()&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;end()&lt;/code&gt;) или slicing-а само за да надникнеш. Тези helper-и четат намерението директно и &lt;em&gt;не&lt;/em&gt; мутират вътрешното състояние на array-а.&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-ни за &lt;code class=&quot;language-text&quot;&gt;reset(&lt;/code&gt; / &lt;code class=&quot;language-text&quot;&gt;end(&lt;/code&gt; / сложни &lt;code class=&quot;language-text&quot;&gt;array_slice(..., 0, 1)&lt;/code&gt; — замени ги със semantic calls. По-чисти diff-ове, по-малко micro-bug-ове.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;7. &lt;code class=&quot;language-text&quot;&gt;get_exception_handler()&lt;/code&gt; (и по-добри fatal traces) – upgrade за observability&lt;/h2&gt;
&lt;p&gt;Framework / infra разработчици, радвайте се: вече можеш да introspect-неш активния exception handler. Chain, wrap, restore или decorate без крехко глобално жонглиране.&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;В комбинация с по-богати stack traces при fatal errors, production post-mortem-ите се ускоряват драматично.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;8. Intl подобрения – човешки списъци и посока&lt;/h2&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;IntlListFormatter&lt;/code&gt; рендерира чаровни, locale-aware conjunctions/disjunctions без ръчно лепене на текст.&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;Комбинирай с &lt;code class=&quot;language-text&quot;&gt;Locale::isRightToLeft()&lt;/code&gt; (или &lt;code class=&quot;language-text&quot;&gt;locale_is_right_to_left()&lt;/code&gt;), за да превключваш layout direction автоматично.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;9. Grapheme-Aware Levenshtein – истинска дистанция между user strings&lt;/h2&gt;
&lt;p&gt;Когато потребителите пишат emoji, accents, combining characters — byte distance или наивна codepoint distance лъже. &lt;code class=&quot;language-text&quot;&gt;grapheme_levenshtein()&lt;/code&gt; уважава &lt;strong&gt;видимите&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;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;Search suggestions, fuzzy match и typo-tolerant login flows стават лингвистично честни.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;10. Парадът на полирането&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; вече ти дава proper object (type safety, бъдещо разширяване) вместо legacy resource.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;cURL Enhancements:&lt;/strong&gt; По-добри share handles + multi-handle introspection = подобрено connection reuse в long-lived workers (мисли RoadRunner, Swoole) и по-фин 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; Бърза проверка „колко стар е този binary?“ за audit scripts. Чудесно за гаранция, че fleet nodes не изостават тихомълком.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;Cheat sheet за feature synergy&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Цел&lt;/th&gt;
&lt;th&gt;Комбинирай&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Pipeline от трансформации с enforced usage&lt;/td&gt;
&lt;td&gt;`&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Declarative validation / strategy maps&lt;/td&gt;
&lt;td&gt;Constant-expression static closures + constant attributes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;По-безопасни refactor-и на legacy arrays&lt;/td&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;array_first()/array_last()&lt;/code&gt; + strict return typing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Production incident triage&lt;/td&gt;
&lt;td&gt;По-добри 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;/tr&gt;
&lt;tr&gt;
&lt;td&gt;International UX polish&lt;/td&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;IntlListFormatter&lt;/code&gt; + direction detection + grapheme distance&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr&gt;
&lt;h2&gt;Практичен план за приемане&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Въведи Pipe Operator постепенно&lt;/strong&gt;: започни в pure data normalization слоеве; налагай style (един side effect на опашката) в code review.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Анотирай критични APIs с &lt;code class=&quot;language-text&quot;&gt;#[\NoDiscard]&lt;/code&gt;&lt;/strong&gt;: фокусирай се първо върху security, persistence и builders — измервай броя warnings в CI.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Refactor-ни Strategy Tables&lt;/strong&gt;: премести простите callable maps в &lt;code class=&quot;language-text&quot;&gt;public const&lt;/code&gt; arrays със static closures за zero-boot cost.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Config Drift Checks&lt;/strong&gt;: добави CI job, който capture-ва output-а на &lt;code class=&quot;language-text&quot;&gt;php --ini=diff&lt;/code&gt;; alert-вай при неочаквани промени.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Metadata Sweep&lt;/strong&gt;: маркирай constants с deprecation / units / feature flags, за да захраниш internal tooling.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Array Edge Extraction Cleanup&lt;/strong&gt;: codemod за замяна на pointer-manipulating patterns.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Error Handler Layering&lt;/strong&gt;: обвий съществуващите global handlers чрез &lt;code class=&quot;language-text&quot;&gt;get_exception_handler()&lt;/code&gt; за observability (Sentry/new relic instrumentation).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;i18n Enhancements&lt;/strong&gt;: смени ръчния “list glue” код с &lt;code class=&quot;language-text&quot;&gt;IntlListFormatter&lt;/code&gt;; тествай RTL layout autoselection.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Fuzzy Matching Quality&lt;/strong&gt;: където се появява user-generated multilingual text (search, tagging), benchmark-ни grapheme спрямо classic distance.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Runtime Audit Script&lt;/strong&gt;: логвай &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; всеки ден, за да хващаш стареещи containers.&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h2&gt;Pitfalls &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;За какво да внимаваш&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 misuse&lt;/td&gt;
&lt;td&gt;Side effects по средата на pipeline&lt;/td&gt;
&lt;td&gt;Ограничаване до pure funcs до final stage&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 (warning blindness)&lt;/td&gt;
&lt;td&gt;Прилагай само върху &lt;em&gt;семантично критични&lt;/em&gt; return-и&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Static closure limits&lt;/td&gt;
&lt;td&gt;Нужен е captured context&lt;/td&gt;
&lt;td&gt;Подай context като explicit param или factory returning closure&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Constant attributes sprawl&lt;/td&gt;
&lt;td&gt;Metadata fragmentation&lt;/td&gt;
&lt;td&gt;Установи internal attribute naming conventions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;i18n list formatting&lt;/td&gt;
&lt;td&gt;Предположения за punctuation styling&lt;/td&gt;
&lt;td&gt;Snapshot tests per locale&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr&gt;
&lt;h2&gt;Мини playground „Show Me“&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;Кога &lt;strong&gt;да не&lt;/strong&gt; посягаш към лъскавото&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Една тривиална трансформация?&lt;/strong&gt; Pipe може да е overkill; &lt;code class=&quot;language-text&quot;&gt;strtolower($x)&lt;/code&gt; още е напълно добре.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Context-heavy closures?&lt;/strong&gt; Обикновени methods с dependency injection &gt; static closure hacks.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Legacy codebase по средата на upgrade?&lt;/strong&gt; Въвеждай по една възможност наведнъж, за да избегнеш cognitive churn.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2&gt;Recap на mental model-а&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;Основен mental model&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;/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;намерена&lt;/em&gt; консумация (използвай или игнорирай с &lt;code class=&quot;language-text&quot;&gt;(void)&lt;/code&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Static closure constants&lt;/td&gt;
&lt;td&gt;Immutable strategy registry, подготвен при load time&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Attributes on constants&lt;/td&gt;
&lt;td&gt;First-class metadata channel за tooling и policies&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;Declarative, non-mutating edge access&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 lens спрямо default baseline&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;Introspect и wrap на global exception flow&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Intl additions&lt;/td&gt;
&lt;td&gt;Вградена locale intelligence вместо handcrafted glue&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Grapheme distance&lt;/td&gt;
&lt;td&gt;Операции върху човешки възприемани символи, не raw codepoints&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Build &amp;#x26; resource polish&lt;/td&gt;
&lt;td&gt;Incremental standardization и introspection&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr&gt;
&lt;h2&gt;Финални vibes&lt;/h2&gt;
&lt;p&gt;PHP 8.5 не крещи с paradigm shifts — &lt;em&gt;шепне&lt;/em&gt; безмилостни ергономични победи. Комбото pipe operator + &lt;code class=&quot;language-text&quot;&gt;#[\NoDiscard]&lt;/code&gt; само по себе си ще побутне кода ти към по-ясно намерение. Добави compile-time closures и constant attributes, и framework-ите/компонентите ти започват да се усещат по-declarative, по-explicit, по-discoverable. Bam bam boom — ship it.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Твой ход:&lt;/strong&gt; Избери една възможност (вероятно pipe-а), приложи я хирургически в малък module, измери яснотата през feedback-а в code review, после разшири. Momentum beats big-bang rewrites.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Остани игрив, refactor-вай смело и — да — пиши на твоите Taylors, когато намериш моментите „Wait, WHAT?!“.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Happy coding.&lt;/strong&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Какво е „добро“ покритие на кода? Практически наръчник]]></title><description><![CDATA[Какво е „добро“ покритие на кода? Моят практически наръчник за спиране на бъгове, без да пилеем инженерно време Всеки път, когато пусна  или…]]></description><link>https://bdteo.com/bg/what-is-good-code-coverage-real-world-guide/</link><guid isPermaLink="false">https://bdteo.com/bg/what-is-good-code-coverage-real-world-guide/</guid><pubDate>Tue, 15 Jul 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;Какво е „добро“ покритие на кода? Моят практически наръчник за спиране на бъгове, без да пилеем инженерно време&lt;/h1&gt;
&lt;p&gt;Всеки път, когато пусна &lt;code class=&quot;language-text&quot;&gt;npm run coverage&lt;/code&gt; или &lt;code class=&quot;language-text&quot;&gt;phpunit --coverage&lt;/code&gt;, изскача един и същ въпрос:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;„Добре... 74%. Достатъчно ли е?“&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Блогосферата за софтуерна разработка крещи „100% или нищо!“. Междувременно &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; учтиво ми напомня, че 100% изпълнен код не е същото като 100% тестван код.&lt;br&gt;
Прекарал съм седмици в гонене на лъскавата метрика и още повече седмици в дебъгване на &lt;em&gt;други&lt;/em&gt; проблеми. Това е проверената в реална работа среда, на която съм се спрял.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;Защо 100% покритие е мираж&lt;/h2&gt;
&lt;p&gt;На теория 100% изпълнение на редовете значи „няма скрити бъгове“. На практика:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Намаляваща възвръщаемост: 90%→95% често удвоява test suite-а ти за едноцифрено намаляване на риска.&lt;/li&gt;
&lt;li&gt;Фалшива увереност: тест, който извиква функция без нито един assertion, &lt;strong&gt;пак се брои&lt;/strong&gt; за покритие.&lt;/li&gt;
&lt;li&gt;Бизнес реалност: всеки допълнителен тест е време, което &lt;strong&gt;не&lt;/strong&gt; отива във функции, поискани от клиентите ти.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Хората в аерокосмическата индустрия могат да се целят в 100% — там е въпрос на живот и смърт. За останалите от нас &lt;strong&gt;~80% е линията 80/20&lt;/strong&gt;. Там се събират повечето проекти след изчисляване на възвръщаемостта. &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; посочва диапазона 70-90% точно по тази причина.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;Практическата таблица, която използвам&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Покритие&lt;/th&gt;
&lt;th&gt;Моят превод&lt;/th&gt;
&lt;th&gt;Действие&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;„Ние сме библиотека, която управлява ракети“&lt;/td&gt;
&lt;td&gt;Приеми мъката.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;90% +&lt;/td&gt;
&lt;td&gt;„Библиотека, от която зависят много пари“&lt;/td&gt;
&lt;td&gt;Само за модул с висок приоритет.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;80%&lt;/td&gt;
&lt;td&gt;Ship it, наблюдавай, после итерация.&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-а, ако новият код те свали под това.&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;Уикенд технически дълг — първо завий към критичните пътища.&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Откраднах тези числа от &lt;a href=&quot;https://www.atlassian.com/continuous-delivery/software-testing/code-coverage&quot; target=&quot;_blank&quot;&gt;вътрешния наръчник на Atlassian&lt;/a&gt;: 60% „приемливо“, 75% „похвално“, 90% „примерно“. Работи във всяка ретроспектива.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;Как стигам до 80%, без да плача (TypeScript playbook)&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;Jest + Istanbul директно от кутията&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Праг за покритие в CI&lt;/strong&gt;&lt;br&gt;
в &lt;code class=&quot;language-text&quot;&gt;jest.config.js&lt;/code&gt; добавям:
&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;Цели се в горещите потребителски пътища, не в Redux boilerplate logger-а.&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h2&gt;Как стигам до 80% в Laravel (PHP playbook)&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;Инсталирай PCOV за скорост в dev среда, Xdebug за branch данни в CI.&lt;/li&gt;
&lt;li&gt;PHPUnit + тези настройки по подразбиране в &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; брой редове чрез &lt;a href=&quot;https://infection.github.io/&quot; target=&quot;_blank&quot;&gt;Infection&lt;/a&gt; — така хващам редовете „покрити, но не наистина тествани“.&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h2&gt;4 правила, по които живее екипът ми&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Нов код = тестове.&lt;/strong&gt; Diff coverage ≥ 90% преди merge.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Първо рефакторирай, после тествай.&lt;/strong&gt; Нетестваемият код вече е дълг.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Проваляй build-а, не хората.&lt;/strong&gt; Сваляй прага с 5% всяка година, вместо да чупиш екипи с червени dashboard-и.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Мери бъговете в production&lt;/strong&gt; — ако покритието е 85%, но инцидентите скачат, &lt;strong&gt;покритието&lt;/strong&gt; не е виновникът; &lt;strong&gt;assertion-ите&lt;/strong&gt; са.&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h2&gt;TL;DR (и за ръководители, и за recruiter-и)&lt;/h2&gt;
&lt;p&gt;Не ме питай за „магическо число“. Попитай:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Кои части от продукта не могат да се счупят?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Покрий &lt;strong&gt;тях&lt;/strong&gt; до 90%. Дай на останалото здрави smoke tests. Използвай code coverage като &lt;strong&gt;прожектор&lt;/strong&gt;, не като финална линия, и вярвай на бъговете, които &lt;strong&gt;хващаш&lt;/strong&gt;, не на числата, с които се &lt;strong&gt;хвалиш&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Нека coverage dashboard-ът бъде зелен — клиентите ти никога няма да го видят, но тяхната лента с грешки ще остане празна.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;— Край на rant-а, обратно към editor-а.&lt;/em&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Моят основен наръчник за ефективни Pull Request ревюта]]></title><description><![CDATA[Като човек, който пише и ревюира много код, научих, че Pull Request (PR) ревютата са повече от проверки за бъгове - те са за споделена…]]></description><link>https://bdteo.com/bg/essential-guide-effective-pull-request-reviews/</link><guid isPermaLink="false">https://bdteo.com/bg/essential-guide-effective-pull-request-reviews/</guid><pubDate>Sun, 06 Jul 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Като човек, който пише и ревюира много код, научих, че Pull Request (PR) ревютата са повече от проверки за бъгове - те са за споделена собственост, прехвърляне на знание и изграждане на по-добър код заедно. Ето кратък, практичен наръчник, който прави PR-ите по-ценни и по-малко болезнени.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;1. Цели на доброто ревю&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Фокус върху подобрение, не върху съвършенство&lt;/strong&gt;&lt;br&gt;
Съвършеният код не е реалистична цел - стреми се към &lt;em&gt;по-добър&lt;/em&gt; код. Ако един PR подобрява четимостта, поддръжката или коректността, одобри го, дори ако остават дребни стилови неща. Използвай &quot;Nit:&quot; за незадължителни предложения.  &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;Споделена собственост и менторство&lt;/strong&gt;&lt;br&gt;
Отнасяй се към PR-ите като към общ код. Оставяй образователна обратна връзка (&quot;Nit: тук можеш да използваш X...&quot;), менторствай junior разработчици и бъди отворен да учиш и от тях.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2&gt;2. Подготовка преди ревю&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Автори&lt;/strong&gt;: Направете self-review: пуснете тестове, линтери и форматъри. Дайте контекст в PR описанията и анотирайте сложната логика.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Ревюиращи&lt;/strong&gt;: Прочетете първо описанието. Разберете &quot;защо&quot; преди да влезете в кода.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2&gt;3. Дръж PR-ите малки и фокусирани&lt;/h2&gt;
&lt;p&gt;Данните показват, че качеството на ревютата пада значително отвъд ~400 LOC и ~60 минути.  &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;Насоки&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Оставай под 200-400 LOC на 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;Дръж ревютата под 60 минути.&lt;/li&gt;
&lt;li&gt;За големи функционалности използвай stacked PR-и (DB -&gt; API -&gt; UI).&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2&gt;4. Назначавай ревюиращите внимателно&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Един основен ревюиращ&lt;/strong&gt;, идеално с познание в домейна.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Максимум двама ревюиращи&lt;/strong&gt;, за да се избегне размиване на отговорността.  &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;Ротирай ревюиращите за cross-training и здрав bus-factor.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2&gt;5. Какво да проверяваш в PR&lt;/h2&gt;
&lt;p&gt;Използвай този мисловен checklist:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Коректност: Изпълнява ли изискванията и покрива ли edge case-ите?&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Дизайн&lt;/strong&gt;: Добре структуриран и идиоматичен ли е?&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Четимост&lt;/strong&gt;: Ясни имена, проста логика, консистентен стил.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Сигурност&lt;/strong&gt;: Валидиране на input-и, sanitize на output-и, избягване на leak-ове.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Performance&lt;/strong&gt;: Внимавай за тежки цикли и N+1 заявки.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Тестове&lt;/strong&gt;: Покритие за основни, edge и error случаи.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Compliance&lt;/strong&gt;: Правилни docs, CI, licensing, formatting.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&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://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. Използвай автоматизацията&lt;/h2&gt;
&lt;p&gt;Остави инструментите да вършат грубата работа:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Линтери (ESLint, RuboCop, SonarQube)&lt;/li&gt;
&lt;li&gt;Форматъри (Prettier, Black)&lt;/li&gt;
&lt;li&gt;CI pipelines с тестове, coverage и проверки за сигурност&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Това позволява на човешките ревюиращи да се фокусират върху логика, архитектура и нюанс.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;7. Давай конструктивна и добра обратна връзка&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Бъди уважителен - поставяй препинателни знаци на предложенията, не на хората.&lt;/li&gt;
&lt;li&gt;Похвали добре свършеното.&lt;/li&gt;
&lt;li&gt;Бъди приложим: обясни &lt;em&gt;защо&lt;/em&gt; и предложи &lt;em&gt;как&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;Prefix-вай non-blocker-и с &quot;Nit:&quot; или &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;Дръж дискусиите обективни (&quot;ние&quot; &gt; &quot;ти&quot;). Избягвай лична критика.&lt;/li&gt;
&lt;li&gt;Предложи синхронен разговор, ако back-and-forth-ът блокира процеса.  &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. Мери процеса, не хората&lt;/h2&gt;
&lt;p&gt;Ключови метрики за следене на тенденции (не за оценяване на хора):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Turnaround time&lt;/strong&gt; (PR open -&gt; merge)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Inspection rate&lt;/strong&gt; (&amp;#x3C; 300-500 LOC/hr е най-добре)  &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;Defect density&lt;/strong&gt; (issues per LOC)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Review coverage&lt;/strong&gt; по компоненти&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Брой follow-up commit-и&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Използвай наблюденията, за да подобряваш workflow-а - например да настояваш за по-малки PR-и, по-добри docs или обучение около трудни модули - но никога не връзвай метриките с 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. Езиково-специфични съображения&lt;/h2&gt;
&lt;p&gt;Различните парадигми изискват различно внимание:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;PHP/JavaScript/TS&lt;/strong&gt;: Async handling, XSS, SOLID principles&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Python&lt;/strong&gt;: Resource management (&lt;code class=&quot;language-text&quot;&gt;with&lt;/code&gt;), PEP 8, default-arg pitfalls&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Haskell/Scala functional&lt;/strong&gt;: Type signatures, purity, immutability, macro checks&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;C/C++&lt;/strong&gt;: Memory safety, pointers, RAII&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Java&lt;/strong&gt;: Null-safety, clean concurrency, SOLID&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Lisp&lt;/strong&gt;: Macro documentation, dynamic typing, idiomatic patterns&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Адаптирай checklist-ите според stack-а си и включвай експерти за непознати езици.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;Bonus: Препоръчани източници за по-дълбоко четене&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Google’s &lt;em&gt;The Standard of Code Review&lt;/em&gt;&lt;/strong&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;&lt;strong&gt;Google Code Review Developer Guide&lt;/strong&gt; - Насоки в checklist стил.  &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 study&lt;/strong&gt; - Емпирични изводи за размера и времето на 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;&lt;strong&gt;Atlassian &quot;5 Code Review Best Practices&quot;&lt;/strong&gt; - Практични съвети за стил и екипна работа.  &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; - Реален staged review процес.  &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;Финални мисли&lt;/h2&gt;
&lt;p&gt;Когато са направени както трябва, PR ревютата са повече от quality gates - те са двигатели за учене, сътрудничество и инженерно майсторство. Като комбинираме уважителна култура, умни инструменти, процес, информиран от данни, и внимателна обратна връзка, code reviews се превръщат в смислени разговори - не в досадни задължения.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Приятно ревюиране!&lt;/strong&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;Остави коментар или ми пиши, ако искаш да влезем по-дълбоко или да споделиш свои съвети за ревюта!&lt;/em&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Засичане на пушене с Apple Watch: как изграждам Still Mirror (Swift, SWT)]]></title><description><![CDATA[Идеята да разбираме истински навиците си, особено онези, които извършваме почти несъзнателно, винаги ме е вълнувала. Ами ако носимите ни…]]></description><link>https://bdteo.com/bg/apple-watch-still-mirror-swift-swt-passive-smoking-detection/</link><guid isPermaLink="false">https://bdteo.com/bg/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;Идеята да разбираме истински навиците си, особено онези, които извършваме почти несъзнателно, винаги ме е вълнувала. Ами ако носимите ни устройства можеха да ни предложат меко, неосъдително огледало за тези модели? Този въпрос запали проекта &quot;Still Mirror&quot;: опит да се засичат пасивно събития на пушене или вейпване чрез богатите физиологични данни от Apple Watch, без потребителят да въвежда нищо ръчно. Това не е опит да се построи поредното приложение за отказване, а инструмент за чисто, неподправено осъзнаване.&lt;/p&gt;
&lt;h2&gt;Предизвикателството: шепот в симфония от шум&lt;/h2&gt;
&lt;p&gt;Основното предизвикателство е огромно: как различаваш фината физиологична следа на пушене/вейпване от безбройните други ежедневни дейности и телесни реакции? Стрес, бърза разходка, стряскащ шум или дори чаша кафе могат да причинят краткотрайни промени в сърдечната честота (HR) и вариабилността на сърдечната честота (HRV). Сигналът, който търсим, често е шепот в симфония от физиологичен шум.&lt;/p&gt;
&lt;p&gt;Но за да изолирам истински тези мимолетни събития, ми трябваше по-сложна техника за обработка на сигнали.&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;Работно място на разработчик с Xcode, показващ Swift код за Apple Watch приложение, с графики от HealthKit данни на заден план.&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;Фиг. 1. – Екосистемата за Apple разработка: Xcode, Swift и HealthKit са в центъра на оживяването на &apos;Still Mirror&apos; върху Apple Watch.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h2&gt;Избор на инструментариум: Apple екосистема и Swift&lt;/h2&gt;
&lt;p&gt;За проект, насочен към Apple Watch, изборът на екосистема е ясен:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Xcode и Swift:&lt;/strong&gt; Нативната среда за разработка за Apple платформи. Това означаваше да навляза по-дълбоко в Swift, език, който намирам за елегантен и мощен, и да се оправям с особеностите на Xcode.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;HealthKit:&lt;/strong&gt; Framework-ът на Apple е входът към основните потоци от данни: сърдечна честота, HRV (SDNN/RMSSD), SpO2 (особено релевантно при горене спрямо вейпване) и нива на активност. Privacy-centric дизайнът на HealthKit е ключов за приложение, което работи с толкова чувствителни данни.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Ограничения на watchOS:&lt;/strong&gt; Разработката за часовника означава постоянно балансиране между функционалност и ресурсни ограничения - батерията и възможностите за background processing винаги са на преден план.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Алгоритмичното сърце: стационарна уейвлет трансформация (SWT)&lt;/h2&gt;
&lt;p&gt;Традиционният анализ на времеви редове често се затруднява с нестационарни сигнали - сигнали, чиито статистически свойства (като средна стойност и дисперсия) се променят във времето. Физиологичните данни са пословично нестационарни. Тук влиза в игра &lt;strong&gt;Stationary Wavelet Transform (SWT)&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;За разлика от стандартната Discrete Wavelet Transform (DWT), която е shift-variant (тоест малко изместване във входния сигнал може драматично да промени уейвлет коефициентите), SWT е shift-invariant. Това я прави по-устойчива при анализ на сигнали, където точният момент на събитията е критичен, но може леко да варира.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Защо SWT за този проблем?&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Time-Frequency Localization:&lt;/strong&gt; SWT може да разложи сигнал на различни честотни ленти, като запази времевата информация. Това означава, че можем да търсим конкретни честотни характеристики (например внезапни изблици на високочестотна активност в HR или специфични промени в HRV честотни ленти), които се случват в точни моменти.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Denoising:&lt;/strong&gt; Физиологичните сигнали са шумни. SWT може да помогне да отделим подлежащия &quot;истински&quot; сигнал от случайния шум чрез анализ на уейвлет коефициенти на различни мащаби.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Засичане на преходни събития:&lt;/strong&gt; Тя е особено добра в разпознаването на резки промени, пикове или преходни събития в сигнал - точно това, което бихме очаквали от острата физиологична реакция към прием на никотин.&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;Абстрактна визуализация на физиологичен сигнал, разложен чрез Stationary Wavelet Transform на множество честотни ленти.&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;Фиг. 2. – Визуализация на Stationary Wavelet Transform, която разлага сигнал на съставните му честотни компоненти във времето и помага за разпознаване на модели.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;По същество SWT действа като сложен набор от филтри, който ни позволява да &quot;видим&quot; модели в HR, HRV и потенциално SpO2 данните, които иначе могат да бъдат скрити от шум или дългосрочни тенденции. Можем да търсим характерни &quot;форми&quot; или промени в енергията в конкретни уейвлет подленти, които съответстват на физиологичния тласък.&lt;/p&gt;
&lt;h2&gt;Пътят на разработката: от данни към засичане&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Събиране на данни (HealthKit):&lt;/strong&gt; Настройване на надеждно фоново извличане на данни от HealthKit, с уважение към потребителските разрешения и ефективна обработка на обновленията.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Предварителна обработка на сигнала:&lt;/strong&gt; Почистване на входящите HR, HRV и SpO2 данни. Това включва обработка на липсващи точки и вероятно начално филтриране преди прилагане на SWT.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Прилагане на SWT:&lt;/strong&gt; Прилагане на Stationary Wavelet Transform върху сегменти от физиологичните времеви редове. Това включва избор на подходящ mother wavelet (например Daubechies, Symlet) и ниво на разлагане.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Извличане на признаци от уейвлет коефициенти:&lt;/strong&gt; Тук се случва магията (и много експериментиране). Вместо да гледаме директно суровите HR/HRV стойности, анализираме SWT коефициентите. Релевантни признаци могат да бъдат:
&lt;ul&gt;
&lt;li&gt;Енергия в конкретни ленти на detail coefficients около момента на подозирано събитие.&lt;/li&gt;
&lt;li&gt;Статистически свойства (дисперсия, ексцес) на коефициентите.&lt;/li&gt;
&lt;li&gt;Cross-correlation между уейвлет коефициенти на различни физиологични сигнали (например HR и HRV).&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Логика/модел за засичане:&lt;/strong&gt; В началото това може да бъде rule-based система, която търси конкретни модели в извлечените уейвлет признаци (например &quot;значим energy spike в HR detail coefficients при scale X, съвпадащ с рязък спад в energy в HRV detail coefficients при scale Y, по време на период с ниска физическа активност&quot;). С времето това може да се развие в machine learning модел, обучен върху тези признаци.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Оценка на увереност:&lt;/strong&gt; Както е описано в моя MVPS алгоритъм, генерирането на confidence score за всяко засечено събитие е ключово, защото отразява силата и яснотата на сигнатурата.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Имплементация на watchOS приложението:&lt;/strong&gt; Изпълнение на основния алгоритъм за засичане върху Apple Watch, с оптимизация за батерия (например обработка на данни на batch-ове, интелигентно задействане на анализ).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;iOS companion app:&lt;/strong&gt; За показване на timeline на засечените събития, предоставяне на insights и управление на настройките. Синхронизацията на данни чрез WatchConnectivity е ключова тук.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;Здравни и етични съображения: философията на &quot;огледалото&quot;&lt;/h2&gt;
&lt;p&gt;Важно е да повторя, че &quot;Still Mirror&quot; е замислен като &lt;em&gt;инструмент за осъзнаване&lt;/em&gt;, не като медицинско устройство или програма за отказване.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Privacy first:&lt;/strong&gt; Цялата обработка, особено чувствителната алгоритмична работа, в идеалния случай трябва да се случва on-device. Достъпът до HealthKit данни е строго permission-based.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Без осъждане:&lt;/strong&gt; Интерфейсът на приложението и всякакви insights, които предоставя, трябва да бъдат неутрални - просто да отразяват модели без предписващи съвети или засрамване.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Точност и прозрачност:&lt;/strong&gt; Потребителите трябва да разбират ограниченията на приложението. False positives и false negatives са неизбежни при толкова сложно пасивно засичане. Прозрачността за увереността на засичанията е важна.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Овластяване на потребителя:&lt;/strong&gt; Целта е да дадем на хората данни за собствените им тела и навици, за да могат сами да вземат информирани решения.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Учене на Swift и навигация в Apple екосистемата&lt;/h2&gt;
&lt;p&gt;За разработчици, които идват основно от други среди (като моите PHP/Laravel корени), навлизането в Swift, SwiftUI, Xcode и конкретните ограничения на watchOS разработката е сериозна крива на учене. Framework-ите на Apple имат своя специфична философия. Управлението на жизнения цикъл на приложенията, background tasks, HealthKit queries и комуникацията между устройства (WatchConnectivity) имат свои модели и &quot;Apple начини&quot; за правене на нещата. Въпреки това богатата документация, силната общност и мощта на Swift правят пътуването възнаграждаващо.&lt;/p&gt;
&lt;h2&gt;Заключение: потенциалът на един мълчалив наблюдател&lt;/h2&gt;
&lt;p&gt;&quot;Still Mirror&quot; все още е изследване - трудно начинание, което опитва да разшири границите на това, което пасивното sensing върху потребителско wearable устройство може да постигне. Stationary Wavelet Transform предлага обещаващ път за разрязване на сложни физиологични сигнали и откриване на фините сигнатури, които търсим.&lt;/p&gt;
&lt;p&gt;Пътят включва не само писане на Swift и борба с Xcode, но и навлизане в теория на обработката на сигнали, разбиране на човешката физиология и внимателно обмисляне на етичните последици от такава технология. Независимо дали &quot;Still Mirror&quot; ще стане широко използвано приложение, или ще остане сложна техническа експедиция, самият процес е свидетелство за увлекателното пресичане на AI, здраве и лична технология. Става дума за опит да се построи онази тиха, отразяваща повърхност - неподвижно огледало - за по-дълбоко самоосъзнаване.&lt;/p&gt;
&lt;p&gt;Какво мислите за използването на advanced signal processing като SWT за пасивно засичане на навици? Ще се радвам да чуя вашите мисли в коментарите по-долу!&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Поправяне на Bluetooth в Android Emulator на M1 Mac с Bumble и API 32]]></title><description><![CDATA[Ако разработвате с Bluetooth на M1/M2/M3 Mac и се опитвате да накарате Bluetooth радиото на хост машината да работи вътре в Android Emulator…]]></description><link>https://bdteo.com/bg/m1-mac-android-emulator-bluetooth-passthrough-bumble/</link><guid isPermaLink="false">https://bdteo.com/bg/m1-mac-android-emulator-bluetooth-passthrough-bumble/</guid><pubDate>Mon, 14 Apr 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Ако разработвате с Bluetooth на M1/M2/M3 Mac и се опитвате да накарате Bluetooth радиото на хост машината да работи вътре в Android Emulator, вероятно вече сте усетили болката. Нещо, което изглежда сякаш &lt;em&gt;трябва&lt;/em&gt; да е право напред, често се превръща в дразнеща дупка от неуспешни връзки, криптични грешки и документационни задънени улици. Наскоро минах през точно тази битка и след няколко стени най-сетне намерих комбинация с Python Bluetooth стека &lt;strong&gt;Bumble&lt;/strong&gt;, която &lt;em&gt;наистина работи&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Това не е поредното теоретично ръководство; това е разказ стъпка по стъпка за това какво се провали и, по-важното, какво &lt;em&gt;успя&lt;/em&gt; при свързването на Bluetooth-а на моя M1 Mac Pro (в моя случай през външен USB dongle, но принципът може да важи и за вътрешни радиа) към Android 12L (API 32) emulator.&lt;/p&gt;
&lt;h2&gt;Целта: истински Bluetooth в емулатора&lt;/h2&gt;
&lt;p&gt;Целта беше проста: Android Emulator да използва физическия Bluetooth controller на моя Mac вместо собствения си ограничен виртуален controller. Това е решаващо за тестване на приложения, които взаимодействат с реални Bluetooth устройства.&lt;/p&gt;
&lt;h2&gt;Инструментът: Bumble влиза в кадър&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/google/bumble&quot;&gt;Bumble&lt;/a&gt; е мощен Python Bluetooth stack. Ключовият му инструмент за тази задача е &lt;code class=&quot;language-text&quot;&gt;bumble-hci-bridge&lt;/code&gt;, който може от едната страна да се свърже с физически HCI (Host Controller Interface), а от другата да го изложи през различни transports (като TCP или gRPC).&lt;/p&gt;
&lt;h2&gt;Опит #1: QEMU socket методът (логичният първи опит)&lt;/h2&gt;
&lt;p&gt;На база общи познания за QEMU и някои по-стари ръководства първият подход беше да използвам emulator flags, за да свържа директно виртуален serial port (подпрян от TCP socket) към HCI bridge.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Стартиране на bridge-а (TCP server mode):&lt;/strong&gt; Свързахме Bumble към физическия dongle (който изненадващо работеше по-добре с &lt;code class=&quot;language-text&quot;&gt;usb:0&lt;/code&gt;, отколкото с конкретния му VID:PID &lt;code class=&quot;language-text&quot;&gt;usb:0b05:17cb&lt;/code&gt; на моята машина - M1 особености!) и го накарахме да слуша на TCP port.&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;Стартиране на емулатора с QEMU flags:&lt;/strong&gt; Променихме emulator launch script-а (първоначално за API 34), за да добавим &lt;code class=&quot;language-text&quot;&gt;-qemu&lt;/code&gt; flags, които насочват virtual serial port (&lt;code class=&quot;language-text&quot;&gt;virtserialport&lt;/code&gt;) към character device (&lt;code class=&quot;language-text&quot;&gt;chardev&lt;/code&gt;), подпряно от TCP socket, свързващ се към 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 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;Резултатът? Частичен успех, окончателен провал:&lt;/strong&gt; С &lt;code class=&quot;language-text&quot;&gt;lsof&lt;/code&gt; виждахме, че QEMU процесът на емулатора &lt;em&gt;наистина&lt;/em&gt; установи TCP връзка към Bumble bridge-а! Android Bluetooth stack-ът &lt;em&gt;вътре&lt;/em&gt; в емулатора обаче така и не изпрати HCI commands през нея. Включването и изключването на Bluetooth в Android settings не правеше нищо. Bridge logs останаха тихи след първоначалната връзка. &lt;strong&gt;Задънена улица.&lt;/strong&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;Опит #2: Default Netsim bridge (следвайки Bumble docs)&lt;/h2&gt;
&lt;p&gt;Документацията на Bumble споменава bridging към &quot;Netsim&quot; gRPC interface-а на емулатора. Netsim (и неговото ядро Root Canal) е по-новата система на емулатора за virtual Bluetooth controller.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Стартиране на bridge-а (Netsim controller mode):&lt;/strong&gt; Настроихме bridge-а да действа като Netsim controller, да слуша на default gRPC port (8554) и да се свърже с физическия dongle.&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;Стартиране на емулатора (default backend):&lt;/strong&gt; Върнахме launch script-а (все още опитвайки с API 34), махнахме &lt;code class=&quot;language-text&quot;&gt;-qemu&lt;/code&gt; flags и добавихме &lt;code class=&quot;language-text&quot;&gt;-packet-streamer-endpoint default&lt;/code&gt;, за да сме сигурни, че се опитва да използва Netsim backend-а.&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;Резултатът? Няма връзка:&lt;/strong&gt; Този път емулаторът тръгна, но Bumble bridge-ът не показа никакви признаци за входяща gRPC връзка от емулатора. Прегледът на emulator logs не разкри очевидни connection errors, но Bluetooth остана неизползваем. &lt;strong&gt;Още една задънена улица.&lt;/strong&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;Опит #3: API downgrade + explicit Netsim endpoint (победителят!)&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;Символичен мост между платформите Apple и Android&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. - Сюрреалистичен пейзаж, в който провалени network cables висят между Apple и Android скални формации, докато един-единствен мост от въжета с Bumble branding успешно свързва двете страни и позволява на светещи data packets да прекосят пропастта.
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Web searches разкриха общи reports за нестабилност с Bluetooth на API 33/34 емулатори и потенциални проблеми с това как емулаторът открива или се свързва с Netsim backend-а, особено когато външен инструмент се опитва да го прихване. Ключът изглежда беше &lt;strong&gt;изрично да кажем на емулатора къде е Netsim gRPC server-ът&lt;/strong&gt; и &lt;strong&gt;да пробваме по-стар API level&lt;/strong&gt;.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Стартиране на bridge-а (Netsim controller mode, explicit port, &lt;code class=&quot;language-text&quot;&gt;usb:0&lt;/code&gt;):&lt;/strong&gt; Същото като в Опит #2, като се уверим, че слуша на известен port (&lt;code class=&quot;language-text&quot;&gt;8554&lt;/code&gt;) и се свързва с физическия dongle чрез index-а (&lt;code class=&quot;language-text&quot;&gt;usb:0&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 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;Промяна и стартиране на емулатора (API 32, explicit endpoint):&lt;/strong&gt; Създадохме &lt;strong&gt;API 32 (Android 12L)&lt;/strong&gt; AVD с Google Play Services (&lt;code class=&quot;language-text&quot;&gt;gplay_32_arm&lt;/code&gt;). Променихме launch script-а да target-ва този AVD и, решаващо, сменихме &lt;code class=&quot;language-text&quot;&gt;-packet-streamer-endpoint&lt;/code&gt; flag-а от &lt;code class=&quot;language-text&quot;&gt;default&lt;/code&gt; към &lt;em&gt;точния&lt;/em&gt; address на нашия 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;Резултатът? Успех!&lt;/strong&gt; Този път проработи!&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Терминалът с &lt;code class=&quot;language-text&quot;&gt;bumble-hci-bridge&lt;/code&gt; започна да показва gRPC connection logs от емулатора малко след launch-а.&lt;/li&gt;
&lt;li&gt;След като емулаторът boot-на, включването на Bluetooth ON в Android Settings доведе до поток от HCI commands (Reset, Read Version, Set Event Mask и т.н.) в bridge terminal-а.&lt;/li&gt;
&lt;li&gt;Scanning за устройства вътре в емулатора успешно използва физическото Bluetooth radio на Mac през ASUS dongle-а!&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;Печелившата рецепта: стъпка по стъпка&lt;/h2&gt;
&lt;p&gt;Ето точната процедура, която проработи на моя M1 Mac Pro с външен ASUS USB-BT500 dongle:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Инсталирайте Bumble:&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;(По желание, но препоръчително) Изключете native USB BT handling-а на macOS:&lt;/strong&gt; Пуснете &lt;em&gt;веднъж&lt;/em&gt; и &lt;strong&gt;reboot&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-а:&lt;/strong&gt; Отворете terminal и пуснете (оставете го да работи):&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;(Проверете, че показва &lt;code class=&quot;language-text&quot;&gt;&gt;&gt;&gt; connected&lt;/code&gt; два пъти).&lt;/em&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Подгответе emulator launch script-а:&lt;/strong&gt; Запазете &lt;em&gt;пълния script&lt;/em&gt;, даден по-долу, като &lt;code class=&quot;language-text&quot;&gt;launch_gapps_avd_api32.sh&lt;/code&gt; (или подобно). Уверете се, че target-ва &lt;strong&gt;API 32&lt;/strong&gt; AVD (ще създаде такъв с име &lt;code class=&quot;language-text&quot;&gt;gplay_32_arm&lt;/code&gt;, ако не съществува) и изрично използва &lt;code class=&quot;language-text&quot;&gt;-packet-streamer-endpoint localhost:8554&lt;/code&gt;. Направете го executable (&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;Пуснете launch script-а:&lt;/strong&gt; Отворете &lt;em&gt;нов&lt;/em&gt; terminal и изпълнете script-а:&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;Проверете:&lt;/strong&gt; След като емулаторът boot-не:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Проверете &lt;code class=&quot;language-text&quot;&gt;bumble-hci-bridge&lt;/code&gt; terminal-а за gRPC и HCI traffic.&lt;/li&gt;
&lt;li&gt;Отидете в Android Settings -&gt; Bluetooth и го включете ON.&lt;/li&gt;
&lt;li&gt;Пробвайте scanning или pairing.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;Успешният launch script (API 32, explicit Netsim endpoint)&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;Ключови изводи за M1 Mac + Emulator + Bumble&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;API level има значение:&lt;/strong&gt; По-новото не винаги е по-добро за emulator compatibility, особено при сложни features като Bluetooth bridging. API 32 изглеждаше по-стабилен за това от API 34 в моите тестове.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Explicit endpoints:&lt;/strong&gt; Не разчитайте на &lt;code class=&quot;language-text&quot;&gt;-packet-streamer-endpoint default&lt;/code&gt;, когато използвате външен bridge като Netsim controller mode-а на Bumble. Насочете емулатора изрично към &lt;code class=&quot;language-text&quot;&gt;localhost:&amp;lt;port&gt;&lt;/code&gt;, където bridge-ът слуша.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Netsim bridge &gt; QEMU socket:&lt;/strong&gt; &lt;code class=&quot;language-text&quot;&gt;android-netsim&lt;/code&gt; bridge mode изглежда по-вероятно да работи с modern emulators от по-нисконивовия &lt;code class=&quot;language-text&quot;&gt;-qemu -chardev socket&lt;/code&gt; метод, въпреки че socket методът &lt;em&gt;може&lt;/em&gt; да установи TCP link.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code class=&quot;language-text&quot;&gt;usb:0&lt;/code&gt; срещу VID:PID:&lt;/strong&gt; На macOS/M1 идентифицирането на USB devices може да е странно. Ако посочването на точния VID:PID се провали неочаквано, пробвайте да използвате index-а &lt;code class=&quot;language-text&quot;&gt;usb:0&lt;/code&gt; (ако приемем, че това е primary/intended device-ът).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Упоритостта се отплаща:&lt;/strong&gt; Това отне няколко опита, комбинирайки insights от документация, web searches и iterative testing. Не се отказвайте твърде лесно!&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Надявам се споделянето на тази конкретна, работеща конфигурация да спести на други разработчици часове раздразнение. Happy coding (and bridging)!&lt;/p&gt;</content:encoded></item><item><title><![CDATA[AI редизайн на блог: как Claude Code преобрази Gatsby сайта ми]]></title><description><![CDATA[Като разработчик, който прекарва повечето си време в backend-а, винаги съм се борил с дизайна. Личният ми блог беше функционален, но…]]></description><link>https://bdteo.com/bg/claude-code-transformed-my-blog-design-in-minutes/</link><guid isPermaLink="false">https://bdteo.com/bg/claude-code-transformed-my-blog-design-in-minutes/</guid><pubDate>Sat, 12 Apr 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Като разработчик, който прекарва повечето си време в backend-а, винаги съм се борил с дизайна. Личният ми блог беше функционален, но изглеждаше сякаш е заседнал в 2010 - базов styling, непоследователни отстояния и цветова схема, която щедро можеше да бъде описана като &quot;минимална&quot;. От месеци се канех да го преработя, но мисълта да се гмурна в CSS и дизайн системи ми докарваше кошмари.&lt;/p&gt;
&lt;p&gt;После пробвах Claude Code, AI coding assistant-а на Anthropic, и перспективата ми напълно се промени.&lt;/p&gt;
&lt;h2&gt;Предизвикателството: моят тъжен, остарял блог&lt;/h2&gt;
&lt;p&gt;Блогът ми е построен с Gatsby.js и макар съдържанието да беше стабилно, представянето го дърпаше надолу:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Непоследователни отстояния из целия сайт&lt;/li&gt;
&lt;li&gt;Слабо responsive поведение на мобилни устройства&lt;/li&gt;
&lt;li&gt;Имплементация на тъмен режим, която едва работеше&lt;/li&gt;
&lt;li&gt;Типография, която изглеждаше аматьорска в най-добрия случай&lt;/li&gt;
&lt;li&gt;Липса на цялостна цветова система или дизайн език&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Имах смътна идея какво искам: нещо модерно, чисто и професионално, което да остави съдържанието ми да изпъкне. Но да преведа тази визия в истински CSS? Там обикновено удрям стената.&lt;/p&gt;
&lt;h2&gt;Влиза Claude Code: моят виртуален дизайн партньор&lt;/h2&gt;
&lt;p&gt;Бях чувал за Claude Code от известно време, но бях скептичен колко много AI наистина може да помогне с дизайн работа. Все пак дизайнът изисква вкус и око за естетика, не само технически знания.&lt;/p&gt;
&lt;p&gt;Реших въпреки това да му дам шанс, с прост prompt: &quot;Please improve my styling by a ton.&quot; Бях готов да инвестирам един час от времето си и около $15, колкото щеше да струва API usage-ът, за да видя какво е възможно.&lt;/p&gt;
&lt;p&gt;Това, което се случи после, искрено ме изненада.&lt;/p&gt;
&lt;h2&gt;Процесът на трансформация&lt;/h2&gt;
&lt;p&gt;Вместо просто да предложи няколко CSS tweak-а, Claude Code подходи към редизайна с педантичността на професионален разработчик:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Фаза на анализ&lt;/strong&gt;: Първо разгледа съществуващите ми стилове, архитектурата на компонентите и цветовата схема, за да разбере с какво работи.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Създаване на дизайн система&lt;/strong&gt;: Вместо да прави повърхностни промени, изгради цялостна дизайн система, включително:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Професионална цветова палитра със semantic variables за светъл и тъмен режим&lt;/li&gt;
&lt;li&gt;Модернизирана типографска скала с правилни responsive настройки&lt;/li&gt;
&lt;li&gt;Последователна система за отстояния, базирана на 4px grid&lt;/li&gt;
&lt;li&gt;Стандартизирани сенки, border radiuses и transitions&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Преработка на компонентите&lt;/strong&gt;: Пренаписа компонентите ми така, че да следват modern best practices:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Създаде responsive card-based layout за blog posts&lt;/li&gt;
&lt;li&gt;Имплементира sticky header с backdrop blur ефекти&lt;/li&gt;
&lt;li&gt;Проектира елегантен theme toggler&lt;/li&gt;
&lt;li&gt;Добави правилна навигация с active states&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Подобрения в достъпността&lt;/strong&gt;: Claude Code не просто направи нещата по-красиви - направи сайта ми по-достъпен:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Добави правилна поддръжка за keyboard navigation&lt;/li&gt;
&lt;li&gt;Имплементира skip-to-content link&lt;/li&gt;
&lt;li&gt;Осигури достатъчен цветови контраст&lt;/li&gt;
&lt;li&gt;Добави правилни ARIA labels&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Оптимизация на performance-а&lt;/strong&gt;: Кодът беше оптимизиран за performance:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Селективно импортира само необходимите Bootstrap компоненти&lt;/li&gt;
&lt;li&gt;Оптимизира организацията на CSS&lt;/li&gt;
&lt;li&gt;Добави hardware-accelerated animations&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;Резултатите, които ми взеха ума&lt;/h2&gt;
&lt;p&gt;Трансформацията беше смайваща. За около час разговор напред-назад и само $15 разходи за Claude API, блогът ми премина от визия на developer side project към професионално проектирано издание. Редизайнът включваше:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Изтънчена цветова система с правилна поддръжка на тъмен режим&lt;/li&gt;
&lt;li&gt;Красива типография, която се мащабира перфектно между устройствата&lt;/li&gt;
&lt;li&gt;Професионални card layouts за blog posts&lt;/li&gt;
&lt;li&gt;Плавни animations и transitions&lt;/li&gt;
&lt;li&gt;Sticky header със стъклени ефекти&lt;/li&gt;
&lt;li&gt;Последователни отстояния из целия сайт&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;За контекст: да наема професионален web designer за това ниво работа вероятно би струвало $1,000-2,000 и би отнело дни или седмици комуникация напред-назад. Вместо това похарчих един час от времето си и $15 в API разходи.&lt;/p&gt;
&lt;p&gt;Но това, което ме впечатли най-много, не беше само визуалното подобрение или спестените пари - беше качеството на кода. Claude Code не просто нахвърля някакви CSS hacks; той създаде цялостна, поддържаема дизайн система, върху която лесно мога да надграждам.&lt;/p&gt;
&lt;h2&gt;Какво научих за AI-assisted design&lt;/h2&gt;
&lt;p&gt;Това преживяване промени перспективата ми за това какво може да прави AI за разработчиците:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;AI е много добър в систематичното мислене&lt;/strong&gt;: Claude Code не просто направи нещата &quot;по-красиви&quot; - той създаде цялостна система с променливи, последователни patterns и правилна организация.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Запълва празнини в знанията&lt;/strong&gt;: Като човек без дълбока CSS експертиза, Claude Code запълни празнините в знанията ми с modern best practices, които нямаше да знам, че трябва да имплементирам.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Кодът е production-quality&lt;/strong&gt;: Styling framework-ът, който създаде, не е само за показ - това е поддържаем, разширяем код, който следва всички модерни стандарти.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Запази съдържанието и структурата ми&lt;/strong&gt;: Claude Code подобри дизайна, като запази основната структура и съдържанието на блога ми.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;Бъдещето на collaboration-а между разработчици и AI&lt;/h2&gt;
&lt;p&gt;Моят опит подчерта нещо важно: Claude Code не заменя разработчиците - той разширява способностите ни. Помогна ми да преодолея личните си ограничения във frontend design, като същевременно ме остави да контролирам общата посока.&lt;/p&gt;
&lt;p&gt;За разработчици, които са по-силни в едни области, отколкото в други (а това не сме ли всички?), AI assistants като Claude Code могат да помогнат да запълним празнините в skill sets-а си.&lt;/p&gt;
&lt;h2&gt;Пробвай го сам&lt;/h2&gt;
&lt;p&gt;Ако се бориш с дизайн или с друг аспект на development-а, силно препоръчвам да дадеш шанс на Claude Code. Преживяването промени не само блога ми, а и усещането ми за това какво е възможно с AI-assisted development.&lt;/p&gt;
&lt;p&gt;Пробвал ли си Claude Code или подобни AI coding assistants? Ще се радвам да чуя за твоя опит в коментарите долу.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Бележка: Самият този блог беше редизайниран с Claude Code, така че това, което виждаш сега, е резултатът от процеса, който описах. Трансформацията от предишния ми дизайн до сегашния отне около час prompting и iteration и струваше приблизително $15 в Claude API usage. Като вземеш предвид традиционните дизайн разходи, това е невероятно value proposition.&lt;/em&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Huawei Watch D2 BLE Pairing: случай за протокол и vendor lock-in]]></title><description><![CDATA[TL;DR: Huawei Watch D2 не използва стандартно BLE pairing. Вместо това изисква 11-стъпков proprietary handshake с custom GATT…]]></description><link>https://bdteo.com/bg/huawei-watch-d2-proprietary-protocol-vendor-lockin/</link><guid isPermaLink="false">https://bdteo.com/bg/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; Huawei Watch D2 не използва стандартно BLE pairing. Вместо това изисква 11-стъпков proprietary handshake с custom GATT characteristics, HMAC-SHA256 key derivation от QR code и application-level encryption. Това е vendor lock-in по дизайн -- принуждава те да влезеш в Huawei Health app-а. Добрата новина: community-то вече го reverse-engineer-на. Gadgetbridge вече поддържа Watch D2, а open-source имплементации като &lt;code class=&quot;language-text&quot;&gt;huawei-lpv2&lt;/code&gt; съществуват. EU DMA също започва да натиска в обратната посока.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Очаквах стандартно Bluetooth pairing. Connect, bond, exchange data -- обичайното. Вместо това получих proprietary cryptographic handshake, който отне седмици reverse-engineering.&lt;/p&gt;
&lt;p&gt;Това се случи, докато строях D2Explorer -- моят проект за свързване на Huawei Watch D2 към Linux и macOS без Huawei Health app-а. След като &lt;a href=&quot;../bluez-pairing-python-agent-workaround-authentication-failed/&quot;&gt;оправих проблемите с BlueZ pairing agent-а&lt;/a&gt; и минах към cross-platform библиотеката SimpleBLE, мислех, че трудната част е приключила. Трудната част още не беше започнала.&lt;/p&gt;
&lt;h2&gt;Какво би очаквал: стандартно BLE Pairing&lt;/h2&gt;
&lt;p&gt;Ето как Bluetooth LE pairing &lt;em&gt;би трябвало&lt;/em&gt; да работи:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Сканираш за устройството по advertised name (например &quot;HUAWEI WATCH D2-CA0&quot;).&lt;/li&gt;
&lt;li&gt;Свързваш се с &lt;code class=&quot;language-text&quot;&gt;peripheral.connect()&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;OS-ът управлява pairing/bonding -- PIN prompt, Just Works, каквото изисква security level-ът.&lt;/li&gt;
&lt;li&gt;След bonding-а работиш със standard или custom GATT services.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;OS-ът управлява security-то. Твоето приложение се фокусира върху data. Просто.&lt;/p&gt;
&lt;h2&gt;Какво всъщност става: 11-стъпков Proprietary Handshake&lt;/h2&gt;
&lt;p&gt;Това, което Watch D2 всъщност изисква, е съвсем различно. Базовата BLE връзка е само вратата. Зад нея има custom application-level authentication protocol, който Huawei е построил върху standard BLE -- това, което community-то нарича &lt;strong&gt;Huawei Link Protocol v2&lt;/strong&gt; &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 mechanisms се заобикалят изцяло. За да се authenticate-неш и да получиш достъп до каквито и да било смислени данни, трябва да минеш през тази sequence върху custom GATT characteristics:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Connect&lt;/strong&gt; -- установяваш basic BLE link.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Enable Notifications&lt;/strong&gt; -- &lt;em&gt;веднага&lt;/em&gt; се subscribe-ваш за notifications върху characteristic &lt;code class=&quot;language-text&quot;&gt;0000fe02-...&lt;/code&gt;. Това е timing-critical -- изпуснеш ли прозореца, часовникът те drop-ва.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;GetLinkParams&lt;/strong&gt; -- &lt;em&gt;веднага&lt;/em&gt; изпращаш custom 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;) към write characteristic &lt;code class=&quot;language-text&quot;&gt;0000fe01-...&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Receive Server Nonce&lt;/strong&gt; -- чакаш notification, който съдържа random challenge-а на часовника.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Derive Secret Key&lt;/strong&gt; -- генерираш client nonce. Комбинираш server nonce, client nonce и &lt;strong&gt;numeric value от QR code-а на часовника&lt;/strong&gt;. Пускаш HMAC-SHA256 (с bytes от QR code value-то като key), за да derive-неш shared &lt;code class=&quot;language-text&quot;&gt;secretKey_&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;AuthRequest&lt;/strong&gt; -- изпращаш client nonce и HMAC digest (с derived &lt;code class=&quot;language-text&quot;&gt;secretKey_&lt;/code&gt;) обратно към часовника (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; -- получаваш authentication token-а на часовника. Проверяваш го със &lt;code class=&quot;language-text&quot;&gt;secretKey_&lt;/code&gt; и обменените nonces.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;SetTime&lt;/strong&gt; -- изпращаш current time и timezone offset, &lt;em&gt;encrypted&lt;/em&gt; със &lt;code class=&quot;language-text&quot;&gt;secretKey_&lt;/code&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; -- изпращаш QR code value-то обратно, &lt;em&gt;encrypted&lt;/em&gt; със &lt;code class=&quot;language-text&quot;&gt;secretKey_&lt;/code&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; -- изпращаш final confirmation, &lt;em&gt;encrypted&lt;/em&gt; със &lt;code class=&quot;language-text&quot;&gt;secretKey_&lt;/code&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; -- едва сега connection-ът е authenticated.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Custom TLV message formats. CRC checks. Service and command IDs. Application-level encryption. Timing, чувствителен до милисекунди. Всичко това се случва &lt;em&gt;над&lt;/em&gt; BLE stack-а, невидимо за standard Bluetooth tools.&lt;/p&gt;
&lt;p&gt;QR code-ът на екрана на часовника е shared secret-ът. Без него не можеш да derive-неш key-а. Без key-а не можеш да се authenticate-неш. Без authentication часовникът не ти дава нищо.&lt;/p&gt;
&lt;h2&gt;Защо Huawei прави това&lt;/h2&gt;
&lt;p&gt;Huawei може да го рамкира като enhanced security. Практическият ефект е &lt;strong&gt;vendor lock-in&lt;/strong&gt;.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Висока бариера за влизане&lt;/strong&gt; -- протоколът е undocumented. Reimplementation изисква reverse-engineering на Huawei Health app-а (13 000+ classes, 64 000+ methods &lt;small&gt;&lt;a href=&quot;#ref2&quot;&gt;[2]&lt;/a&gt;&lt;/small&gt;) или анализ на BLE traffic. Това активно обезкуражава third-party apps.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Няма interoperability&lt;/strong&gt; -- standard fitness apps не могат да се свържат. Часовникът завършва handshake-а си само със software, който знае proprietary steps -- основно собствения Huawei Health app.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Контрол върху ecosystem-а&lt;/strong&gt; -- потребителите са принудени да използват Huawei Health и cloud services на Huawei. Смяната на устройства или платформи по-късно означава да изгубиш историята на health data-та си.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;По-малък избор за потребителя&lt;/strong&gt; -- искаш да използваш open-source app? Искаш повече privacy control върху health data-та си? Лош късмет -- освен ако някой първо не reverse-engineer-не протокола.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;И ето го важното: &lt;strong&gt;това не е уникално за Huawei&lt;/strong&gt;. Research проектът WatchWitch &lt;small&gt;&lt;a href=&quot;#ref3&quot;&gt;[3]&lt;/a&gt;&lt;/small&gt; документира как всички големи vendors -- Apple, Samsung, Xiaomi -- използват proprietary BLE protocols, за да налагат ecosystem lock-in. Apple Watch е &quot;incredibly tightly coupled with Apple&apos;s iPhone and iCloud ecosystem, using proprietary protocols that are unavailable to third parties.&quot; Това е системен проблем в индустрията.&lt;/p&gt;
&lt;p&gt;Но имплементацията на Huawei е особено агресивна. BLE &lt;em&gt;позволява&lt;/em&gt; custom services, разбира се. Но да замениш фундаменталния authentication mechanism с proprietary gatekeeper е съвсем друга игра.&lt;/p&gt;
&lt;h2&gt;Security иронията&lt;/h2&gt;
&lt;p&gt;Очевидната защита е &quot;правим го за security.&quot; Нека погледнем това.&lt;/p&gt;
&lt;p&gt;BlueDoor vulnerability research от Tsinghua University &lt;small&gt;&lt;a href=&quot;#ref4&quot;&gt;[4]&lt;/a&gt;&lt;/small&gt; тества 16 BLE devices, включително Honor Band 3 (същият Huawei ecosystem), и постига &lt;strong&gt;silent pairing without user authorization&lt;/strong&gt; при повечето от тях. Proprietary protocol-ът не го предотвратява.&lt;/p&gt;
&lt;p&gt;Междувременно самият протокол е reverse-engineer-ван многократно -- от Gadgetbridge community-то, от проекта &lt;code class=&quot;language-text&quot;&gt;huawei-lpv2&lt;/code&gt;, от researchers, които представят на Easterhegg 2019 &lt;small&gt;&lt;a href=&quot;#ref2&quot;&gt;[2]&lt;/a&gt;&lt;/small&gt;, и от мен за D2Explorer. Security through obscurity с expiration date.&lt;/p&gt;
&lt;p&gt;HMAC-SHA256 key derivation-ът от QR code-а всъщност е прилична криптография. Но не това е смисълът. Можеш да постигнеш същите security properties със standard BLE Secure Connections и out-of-band pairing method (като NFC или QR code) -- без междувременно да заключваш всяко third-party application отвън.&lt;/p&gt;
&lt;h2&gt;Community-то отвръща&lt;/h2&gt;
&lt;p&gt;Community-то не е приело това тихо.&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; -- open-source Android app-ът за wearable devices -- вече поддържа Huawei Watch D2 &lt;small&gt;&lt;a href=&quot;#ref5&quot;&gt;[5]&lt;/a&gt;&lt;/small&gt;. Можеш да pair-неш часовника си без Huawei Health app-а. Това изискваше сериозен reverse-engineering effort (виж PR #2462 &lt;small&gt;&lt;a href=&quot;#ref6&quot;&gt;[6]&lt;/a&gt;&lt;/small&gt;), и има ограничения -- ECG functionality е disabled, когато часовникът е paired с Gadgetbridge &lt;small&gt;&lt;a href=&quot;#ref7&quot;&gt;[7]&lt;/a&gt;&lt;/small&gt; -- но работи.&lt;/p&gt;
&lt;p&gt;Authentication implementation-ът в Gadgetbridge поддържа auth version 3, като изчислява bonding key-а от pairing message-а (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;) и го използва за decryption. За authentication key negotiation е нужен 17-digit Huawei account ID.&lt;/p&gt;
&lt;h3&gt;huawei-lpv2&lt;/h3&gt;
&lt;p&gt;Проектът &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; предоставя pure Python implementation на Huawei Link Protocol v2 &lt;small&gt;&lt;a href=&quot;#ref8&quot;&gt;[8]&lt;/a&gt;&lt;/small&gt;. Поддържан е, има multiple forks и служи като reference за всеки, който строи Huawei wearable integrations извън official ecosystem-а.&lt;/p&gt;
&lt;h3&gt;D2Explorer&lt;/h3&gt;
&lt;p&gt;Моят собствен D2Explorer проект тръгна по различен път -- C++ implementation със SimpleBLE, която работи на Linux и macOS. Работата включваше:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Implementing TLV serialization/deserialization (&lt;code class=&quot;language-text&quot;&gt;HuaweiProtocol&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Building precise message constructors (&lt;code class=&quot;language-text&quot;&gt;ProtocolMessageBuilder&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Getting the cryptographic steps right -- nonce generation, HMAC-SHA256, XOR encryption (&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;Managing strict state transitions and timing (&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;Debugging failures caused by millisecond-level timing mismatches and subtle crypto errors.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;D2Explorer съществува &lt;em&gt;защото&lt;/em&gt; протоколът на Huawei го направи необходим. Това е workaround-ът, който се изисква за basic functionality извън walled garden-а.&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; стартира през февруари 2026 като major update на open-source Linux-based smartwatch OS &lt;small&gt;&lt;a href=&quot;#ref9&quot;&gt;[9]&lt;/a&gt;&lt;/small&gt;. Вече поддържа ~30 devices, включително Huawei Watch и Huawei Watch 2, с features като always-on display и Tilt-to-Wake. Пълна open-source алтернатива на firmware-а на Huawei.&lt;/p&gt;
&lt;h2&gt;Регулаторната вълна&lt;/h2&gt;
&lt;p&gt;EU не просто наблюдава. Digital Markets Act (DMA) започва да принуждава промяна &lt;small&gt;&lt;a href=&quot;#ref10&quot;&gt;[10]&lt;/a&gt;&lt;/small&gt;.&lt;/p&gt;
&lt;p&gt;През декември 2025 Apple пусна iOS 26.3 с AirPods-like pairing за third-party devices -- включително Huawei smartwatches -- конкретно за да се съобрази с DMA requirements &lt;small&gt;&lt;a href=&quot;#ref11&quot;&gt;[11]&lt;/a&gt;&lt;/small&gt;. Background syncing между Huawei watches и iPhones вече работи в Европа.&lt;/p&gt;
&lt;p&gt;DMA задължава gatekeepers да предоставят interoperability за connected devices. Това директно се прицелва в вида proprietary BLE lock-in, който Huawei (и Apple, и всички останали) практикуват. Full rollout на тези interoperability features се очаква през 2026.&lt;/p&gt;
&lt;p&gt;Това е значимо. За първи път има регулаторен натиск да се стандартизира нещо, което vendors умишлено държат proprietary. Technical community-то може да reverse-engineer-ва протоколи един по един, но regulation може да промени incentive structure-а за цялата индустрия.&lt;/p&gt;
&lt;h2&gt;Какво означава това&lt;/h2&gt;
&lt;p&gt;Pairing protocol-ът на Huawei Watch D2 е case study за това как custom protocols върху standard transports могат да налагат vendor lock-in. Слоевете proprietary cryptography, custom message formats и timing-sensitive handshakes съществуват не защото standard BLE не може да се справи с authentication -- може -- а защото proprietary protocols държат потребителите вътре в ecosystem-а.&lt;/p&gt;
&lt;p&gt;Картината обаче се променя. Gadgetbridge ти дава алтернатива още сега. EU DMA налага interoperability на регулаторно ниво. А open-source проекти като &lt;code class=&quot;language-text&quot;&gt;huawei-lpv2&lt;/code&gt;, D2Explorer и AsteroidOS доказват, че community-то ще reverse-engineer-не това, което vendors се опитват да заключат.&lt;/p&gt;
&lt;p&gt;Строенето на D2Explorer беше по-малко за Bluetooth и повече за cryptographic detective work. То подчертава нещо, което не би трябвало да има нужда от подчертаване: трябва да можеш да достъпваш собствените си health data със software по свой избор.&lt;/p&gt;
&lt;hr&gt;
&lt;h3&gt;References&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 reference implementation на протокола.&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 conference talk, който документира reverse engineering effort-а. &lt;a href=&quot;https://www.sba-research.org/wp-content/uploads/2019/04/easterhegg19.pdf&quot;&gt;Slides (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;Документира как всички големи vendors използват proprietary protocols за ecosystem lock-in.&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;Открива silent pairing vulnerabilities в 16 BLE devices, включително 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;Official support page за Huawei и 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;Pull request-ът, който добавя Huawei device support към Gadgetbridge.&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;Known limitation при използване на Gadgetbridge вместо 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;Step-by-step pairing instructions за Huawei devices.&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 OS, която вече поддържа ~30 devices, включително Huawei watches.&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 provisions, които задължават connected device interoperability.&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;Apple&apos;s compliance с EU interoperability mandates за wearable devices.&lt;/em&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h3&gt;Related Posts&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; -- предшественикът на това разследване. Преди да можехме да се заемем с proprietary protocol-а на Huawei, трябваше да оправим &lt;code class=&quot;language-text&quot;&gt;AuthenticationFailed&lt;/code&gt; errors на BlueZ със standard BLE pairing.&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; -- още една BLE integration битка, този път bridge-вайки physical Bluetooth radio-то на Mac към Android Emulator.&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[Поправка за BlueZ сдвояване: външен Python agent и D-Bus polling]]></title><description><![CDATA[TL;DR: Ако получаваш  със собствен C++/sd-bus pairing agent на BlueZ 5.66+, проблемът най-вероятно е във вътрешната регистрация на agent-а…]]></description><link>https://bdteo.com/bg/bluez-pairing-python-agent-workaround-authentication-failed/</link><guid isPermaLink="false">https://bdteo.com/bg/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; Ако получаваш &lt;code class=&quot;language-text&quot;&gt;org.bluez.Error.AuthenticationFailed&lt;/code&gt; със собствен C++/sd-bus pairing agent на BlueZ 5.66+, проблемът най-вероятно е във вътрешната регистрация на agent-а. Пусни външен Python agent (&lt;code class=&quot;language-text&quot;&gt;simple-agent.py&lt;/code&gt;) като отделен процес и имплементирай D-Bus property polling, вместо да разчиташ на &lt;code class=&quot;language-text&quot;&gt;PropertiesChanged&lt;/code&gt; сигнали. Подробностите и кодът са по-долу.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Прекарах два дни, втренчен в &lt;code class=&quot;language-text&quot;&gt;org.bluez.Error.AuthenticationFailed&lt;/code&gt;, преди да разбера какво всъщност става.&lt;/p&gt;
&lt;p&gt;Pairing agent-ът беше регистриран. D-Bus извикванията изглеждаха правилни. &lt;code class=&quot;language-text&quot;&gt;busctl&lt;/code&gt; потвърждаваше, че всичко е на мястото си -- а BlueZ просто продължаваше да казва не. Това беше по време на работата по &lt;a href=&quot;../huawei-watch-d2-proprietary-protocol-vendor-lockin/&quot;&gt;D2Explorer&lt;/a&gt; -- инструмент за сдвояване с Huawei Watch D2 под Linux -- и грешката при сдвояване блокираше всичко.&lt;/p&gt;
&lt;p&gt;Ето какво наистина се случи и как го оправихме.&lt;/p&gt;
&lt;h2&gt;Планът: вътрешен C++ pairing agent&lt;/h2&gt;
&lt;p&gt;Идеята беше чиста и самодостатъчна. Едно C++ приложение, което управлява целия процес на сдвояване чрез &lt;code class=&quot;language-text&quot;&gt;sd-bus&lt;/code&gt; (C/C++ D-Bus bindings):&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Свържи се към system D-Bus.&lt;/li&gt;
&lt;li&gt;Намери Bluetooth адаптера (&lt;code class=&quot;language-text&quot;&gt;org.bluez.Adapter1&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Имплементирай C++ клас, който предоставя интерфейса &lt;code class=&quot;language-text&quot;&gt;org.bluez.Agent1&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Регистрирай agent-а в &lt;code class=&quot;language-text&quot;&gt;org.bluez.AgentManager1&lt;/code&gt; чрез &lt;code class=&quot;language-text&quot;&gt;RegisterAgent&lt;/code&gt; и &lt;code class=&quot;language-text&quot;&gt;RequestDefaultAgent&lt;/code&gt;. Започнахме с capability &lt;code class=&quot;language-text&quot;&gt;DisplayYesNo&lt;/code&gt;, после го опростихме до &lt;code class=&quot;language-text&quot;&gt;NoInputNoOutput&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Открий целевото устройство (&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; върху D-Bus интерфейса на устройството.&lt;/li&gt;
&lt;li&gt;Вътрешният agent обработва 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;) автоматично -- без нужда от потребителско действие.&lt;/li&gt;
&lt;li&gt;Trust-ваш устройството, установяваш GATT connection, готово.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Един binary, без външни зависимости. Това беше планът.&lt;/p&gt;
&lt;h2&gt;Стената: &lt;code class=&quot;language-text&quot;&gt;org.bluez.Error.AuthenticationFailed&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;Всичко работеше до стъпка 6. Адаптерът беше намерен, agent-ът регистриран (D-Bus го потвърждаваше), устройството открито. Но в момента, в който извикахме &lt;code class=&quot;language-text&quot;&gt;Device1.Pair()&lt;/code&gt; чрез &lt;code class=&quot;language-text&quot;&gt;sd_bus_call_method&lt;/code&gt; -- моментален провал:&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;Опитахме всичко. Различни agent capabilities. Проверихме &lt;code class=&quot;language-text&quot;&gt;sd-bus&lt;/code&gt; vtable настройката. Уверихме се, че имплементациите на agent methods връщат успех бързо. Използвахме &lt;code class=&quot;language-text&quot;&gt;busctl&lt;/code&gt; и &lt;code class=&quot;language-text&quot;&gt;gdbus&lt;/code&gt;, за да наблюдаваме D-Bus traffic-а -- registration calls изглеждаха правилни. &lt;code class=&quot;language-text&quot;&gt;Pair()&lt;/code&gt; извикването просто продължаваше да се проваля.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Задънена улица.&lt;/strong&gt;&lt;/p&gt;
&lt;h2&gt;Пробивът: външен Python agent&lt;/h2&gt;
&lt;p&gt;За да изолираме проблема, извадихме вътрешния C++ agent от уравнението. Пуснахме стандартния &lt;code class=&quot;language-text&quot;&gt;simple-agent.py&lt;/code&gt; на BlueZ като отделен процес &lt;em&gt;преди&lt;/em&gt; да стартираме нашето C++ приложение (вече без собствената му agent registration):&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;Резултатът:&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;Консистентно. Всеки път. Грешката &lt;code class=&quot;language-text&quot;&gt;AuthenticationFailed&lt;/code&gt; изчезна напълно.&lt;/p&gt;
&lt;p&gt;Това доказа, че проблемът не беше в самия &lt;code class=&quot;language-text&quot;&gt;Pair()&lt;/code&gt;, нито в устройството, нито в pairing capability-то на BlueZ. Беше конкретно в начина, по който нашето C++ приложение, използвайки &lt;code class=&quot;language-text&quot;&gt;sd-bus&lt;/code&gt;, се регистрираше и взаимодействаше като pairing agent. Същата логическа операция -- регистриране на &lt;code class=&quot;language-text&quot;&gt;NoInputNoOutput&lt;/code&gt; agent и извикване на &lt;code class=&quot;language-text&quot;&gt;Pair()&lt;/code&gt; -- работеше перфектно, когато agent-ът вървеше като отделен Python процес.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Това сработи.&lt;/strong&gt;&lt;/p&gt;
&lt;h2&gt;Защо вътрешният agent се провали?&lt;/h2&gt;
&lt;p&gt;Когато за пръв път се сблъсках с това, имах само хипотези. Оттогава намерих реални документирани доказателства, че това е по-широк проблем -- не само нашият код.&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; документира случаи, в които устройства се сдвояват без проблем на BlueZ 5.50, но се провалят на по-нови версии с &lt;code class=&quot;language-text&quot;&gt;auth failed with status 0x05&lt;/code&gt;. HCI logs показват &lt;code class=&quot;language-text&quot;&gt;Status: PIN or Key Missing (0x06)&lt;/code&gt; въпреки запазени link keys. Workaround-ът? Стартиране на legacy &lt;code class=&quot;language-text&quot;&gt;bluez-simple-agent.py&lt;/code&gt; script. Звучи познато?&lt;/p&gt;
&lt;h3&gt;Наличният agent е root cause-ът&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; го прави още по-ясно: сдвояването работи само когато &lt;code class=&quot;language-text&quot;&gt;bluetoothctl&lt;/code&gt; или GNOME Bluetooth е стартиран, защото тези приложения регистрират нужния authentication agent. Без активен, &lt;em&gt;правилно функциониращ&lt;/em&gt; agent, BlueZ вътрешно връща &lt;code class=&quot;language-text&quot;&gt;No agent available for request type 2&lt;/code&gt; -- което на повърхността излиза като &lt;code class=&quot;language-text&quot;&gt;AuthenticationFailed&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Ключовият извод: не е достатъчно просто да &lt;em&gt;регистрираш&lt;/em&gt; agent. Agent-ът трябва да отговаря на callbacks от BlueZ по начин, който &lt;code class=&quot;language-text&quot;&gt;bluetoothd&lt;/code&gt; приема за валиден. И нещо в начина, по който &lt;code class=&quot;language-text&quot;&gt;sd-bus&lt;/code&gt; прави това в същия процес, който инициира сдвояването, не удовлетворява по-новите версии на BlueZ.&lt;/p&gt;
&lt;h3&gt;Може дори да не е 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; разкрива, че някои &lt;code class=&quot;language-text&quot;&gt;AuthenticationFailed&lt;/code&gt; грешки са kernel-related, а не BlueZ-related. Kernel 5.9 е имал проблеми със сдвояването, които 5.8.18 и 5.10+ не са имали. Коментарът на maintainer-а си струва да се цитира: &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; документира още един ъгъл: някои устройства (особено iOS) се провалят при сдвояване с &lt;code class=&quot;language-text&quot;&gt;NoInputNoOutput&lt;/code&gt; agents, защото downgrade-ват Secure Connections към Legacy pairing, което води до &lt;code class=&quot;language-text&quot;&gt;Insufficient Authentication (0x05)&lt;/code&gt; грешки при последващ attribute access. Това е Security Manager Protocol (SMP) negotiation проблем, не agent registration проблем -- но произвежда същото error message.&lt;/p&gt;
&lt;h3&gt;Най-вероятните виновници в нашия случай&lt;/h3&gt;
&lt;p&gt;Като се има предвид evidence-ът, най-вероятните обяснения за провала на вътрешния &lt;code class=&quot;language-text&quot;&gt;sd-bus&lt;/code&gt; agent са:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Timing&lt;/strong&gt; -- &lt;code class=&quot;language-text&quot;&gt;sd-bus&lt;/code&gt; registration или method handling в нашия event loop не отговаряше в точния прозорец, който &lt;code class=&quot;language-text&quot;&gt;bluetoothd&lt;/code&gt; очакваше.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Тънкости между &lt;code class=&quot;language-text&quot;&gt;sd-bus&lt;/code&gt; и &lt;code class=&quot;language-text&quot;&gt;python-dbus&lt;/code&gt;&lt;/strong&gt; -- разлики в начина, по който тези libraries взаимодействат с D-Bus daemon-а или управляват object lifetimes.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;По-строги изисквания в BlueZ 5.66+&lt;/strong&gt; -- променени вътрешни sequences за agent interaction, които &lt;code class=&quot;language-text&quot;&gt;sd-bus&lt;/code&gt;, когато се използва в същото приложение, иницииращо сдвояването, не удовлетворява.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;Втората стена: D-Bus сигналите са ненадеждни&lt;/h2&gt;
&lt;p&gt;Да минем покрай &lt;code class=&quot;language-text&quot;&gt;AuthenticationFailed&lt;/code&gt; беше голяма победа, но не беше краят. С външния agent на място &lt;code class=&quot;language-text&quot;&gt;Pair()&lt;/code&gt; успяваше -- но не можехме надеждно да &lt;em&gt;засечем&lt;/em&gt; кога е приключил.&lt;/p&gt;
&lt;p&gt;Разчитахме на D-Bus &lt;code class=&quot;language-text&quot;&gt;PropertiesChanged&lt;/code&gt; сигнали (чрез &lt;code class=&quot;language-text&quot;&gt;sd-bus&lt;/code&gt;), за да разберем кога &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; стават &lt;code class=&quot;language-text&quot;&gt;true&lt;/code&gt;. Понякога сигналите пристигаха. Понякога закъсняваха. Понякога изобщо не пристигаха.&lt;/p&gt;
&lt;p&gt;Затова имплементирахме &lt;strong&gt;active polling&lt;/strong&gt; -- fallback, който query-ва property values директно, когато сигналите не се появят:&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;Всеки state transition method (&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;) следва същия pattern: първо проверява cached atomic boolean-а (обновен от signal handler-а, ако той сработи), после пада обратно към директен D-Bus &lt;code class=&quot;language-text&quot;&gt;Get&lt;/code&gt; property call.&lt;/p&gt;
&lt;p&gt;Не е елегантно. Но е необходимо.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Това сработи.&lt;/strong&gt;&lt;/p&gt;
&lt;h2&gt;Пълната поправка&lt;/h2&gt;
&lt;p&gt;Ето консолидираната рецепта. Ако изграждаш automated Bluetooth pairing под Linux с BlueZ 5.66+ и удряш &lt;code class=&quot;language-text&quot;&gt;AuthenticationFailed&lt;/code&gt;:&lt;/p&gt;
&lt;h3&gt;Стъпка 1: Вземи simple-agent.py&lt;/h3&gt;
&lt;p&gt;Вземи го от &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;Стъпка 2: Стартирай външния agent&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;Дръж го стартиран в отделен terminal (или като background service).&lt;/p&gt;
&lt;h3&gt;Стъпка 3: Извади вътрешния agent от приложението си&lt;/h3&gt;
&lt;p&gt;Премахни всички &lt;code class=&quot;language-text&quot;&gt;RegisterAgent&lt;/code&gt; / &lt;code class=&quot;language-text&quot;&gt;RequestDefaultAgent&lt;/code&gt; извиквания от C++ приложението си. Остави външния Python agent да обработва authentication callbacks.&lt;/p&gt;
&lt;h3&gt;Стъпка 4: Добави D-Bus property polling&lt;/h3&gt;
&lt;p&gt;Не разчитай само на &lt;code class=&quot;language-text&quot;&gt;PropertiesChanged&lt;/code&gt; сигнали. За всяко критично 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;) имплементирай cache-then-poll pattern-а, показан по-горе. Poll-вай периодично от main loop-а.&lt;/p&gt;
&lt;h3&gt;Стъпка 5: Провери&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;Потвърди, че външният agent е стартиран (&lt;code class=&quot;language-text&quot;&gt;sudo python simple-agent.py NoInputNoOutput&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Стартирай приложението си. &lt;code class=&quot;language-text&quot;&gt;Pair()&lt;/code&gt; трябва да успее.&lt;/li&gt;
&lt;li&gt;Наблюдавай polling logs -- трябва да виждаш D-Bus property queries за state transitions.&lt;/li&gt;
&lt;li&gt;Ако &lt;code class=&quot;language-text&quot;&gt;Pair()&lt;/code&gt; все още се проваля, провери версията на BlueZ (&lt;code class=&quot;language-text&quot;&gt;bluetoothd --version&lt;/code&gt;) и kernel версията -- проблемът може да е по-дълбок.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;Какво ти струва това&lt;/h2&gt;
&lt;p&gt;Няма да се преструвам, че това е чисто решение. Не е:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;External dependency&lt;/strong&gt; -- приложението ти вече има нужда от отделен Python процес, който да върви.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Повече complexity&lt;/strong&gt; -- polling logic в main loop-а, върху signal handlers.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;По-малко self-contained&lt;/strong&gt; -- мечтата за един binary си отиде.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Но работи. Надеждно. А когато два дни си гледал &lt;code class=&quot;language-text&quot;&gt;AuthenticationFailed&lt;/code&gt;, &quot;работи&quot; е това, което има значение.&lt;/p&gt;
&lt;hr&gt;
&lt;h3&gt;Референции&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;Свързани публикации&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; -- проектът, който доведе до това разследване. Watch D2 изисква proprietary application-level handshake върху стандартния BLE pairing, затова automated pairing трябваше да заработи още от самото начало.&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; -- още една Bluetooth integration битка, този път bridge-ване на физическото radio на Mac към Android Emulator.&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[Dev Guide: промени в класове и namespace-и в Shopware 6.5/6.6]]></title><description><![CDATA[Shopware 6.5 и 6.6 въведоха няколко съществени промени в класове, namespace-и, data attributes и механизми за сигурност, с които…]]></description><link>https://bdteo.com/bg/understanding-class-namespace-changes-shopware-6-5-developers-guide/</link><guid isPermaLink="false">https://bdteo.com/bg/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 и 6.6 въведоха няколко съществени промени в класове, namespace-и, data attributes и механизми за сигурност, с които разработчиците трябва да са наясно, когато обновяват или поддържат своите Shopware проекти. Тази статия дава стегнат, но достатъчно пълен преглед на тези промени, заедно с наблюдения за влиянието им и за това как да адаптираш кода си.&lt;/p&gt;
&lt;h2&gt;Въведение&lt;/h2&gt;
&lt;p&gt;Докато Shopware се развива, обновленията често носят подобрения, оптимизации и нови възможности. Но понякога въвеждат и промени, които могат да засегнат съществуващи codebase-и. Разбирането им е решаващо за плавен преход и за смислено използване на новите възможности.&lt;/p&gt;
&lt;p&gt;Тази статия се фокусира върху следните ключови области:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Миграция на Elasticsearch namespace-а&lt;/li&gt;
&lt;li&gt;Промени при обработката на media paths&lt;/li&gt;
&lt;li&gt;Промени в методите на &lt;code class=&quot;language-text&quot;&gt;AvailableCombinationLoader&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Ъпгрейд на Symfony framework-а до версия 6&lt;/li&gt;
&lt;li&gt;Промени при stock handling&lt;/li&gt;
&lt;li&gt;Ъпгрейд на Storefront към Bootstrap&lt;/li&gt;
&lt;li&gt;Промени в data attribute-а за Offcanvas Cart&lt;/li&gt;
&lt;li&gt;Промени в CSRF защитата&lt;/li&gt;
&lt;li&gt;Подобрения в Rule Builder&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Нека разгледаме всяка от тези промени по-подробно.&lt;/p&gt;
&lt;h2&gt;1. Миграция на Elasticsearch namespace-а&lt;/h2&gt;
&lt;h3&gt;Преглед на промяната&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Предишен 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;Нов 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;Влияние&lt;/h3&gt;
&lt;p&gt;Всички класове и методи, които взаимодействат с Elasticsearch, трябва да обновят namespace-ите си, за да отразят миграцията от &lt;code class=&quot;language-text&quot;&gt;ONGR\ElasticsearchDSL&lt;/code&gt; към &lt;code class=&quot;language-text&quot;&gt;OpenSearchDSL&lt;/code&gt;.&lt;/p&gt;
&lt;h3&gt;Необходимо действие&lt;/h3&gt;
&lt;p&gt;Обнови import statements и reference-и в кода си, така че да използват новия namespace.&lt;/p&gt;
&lt;h3&gt;Пример&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;Наблюдения&lt;/h3&gt;
&lt;p&gt;Тази промяна е в синхрон с по-широкото индустриално движение към OpenSearch, community-driven fork на Elasticsearch. Обновяването към новия namespace гарантира съвместимост с бъдещото развитие и поддръжка.&lt;/p&gt;
&lt;h2&gt;2. Media Path Handling&lt;/h2&gt;
&lt;h3&gt;Преглед на промяната&lt;/h3&gt;
&lt;p&gt;Media paths вече се съхраняват директно в базата данни, вместо да се генерират динамично.&lt;/p&gt;
&lt;h3&gt;Влияние&lt;/h3&gt;
&lt;p&gt;Класове и услуги, които преди са разчитали на динамично генериране на пътища, трябва да четат media paths от базата данни.&lt;/p&gt;
&lt;h3&gt;Необходимо действие&lt;/h3&gt;
&lt;p&gt;Адаптирай кода си така, че да използва метода &lt;code class=&quot;language-text&quot;&gt;getPath()&lt;/code&gt; от &lt;code class=&quot;language-text&quot;&gt;MediaEntity&lt;/code&gt;.&lt;/p&gt;
&lt;h3&gt;Пример&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;Наблюдения&lt;/h3&gt;
&lt;p&gt;Съхраняването на media paths в базата данни подобрява производителността, като намалява изчислителния overhead. Освен това дава повече консистентност и надеждност при работа с media.&lt;/p&gt;
&lt;h2&gt;3. Промяна в метод на &lt;code class=&quot;language-text&quot;&gt;AvailableCombinationLoader&lt;/code&gt;&lt;/h2&gt;
&lt;h3&gt;Преглед на промяната&lt;/h3&gt;
&lt;p&gt;Методът &lt;code class=&quot;language-text&quot;&gt;load()&lt;/code&gt; в &lt;code class=&quot;language-text&quot;&gt;AbstractAvailableCombinationLoader&lt;/code&gt; е заменен с &lt;code class=&quot;language-text&quot;&gt;loadCombinations()&lt;/code&gt;.&lt;/p&gt;
&lt;h3&gt;Влияние&lt;/h3&gt;
&lt;p&gt;Всички custom класове, които extend-ват &lt;code class=&quot;language-text&quot;&gt;AbstractAvailableCombinationLoader&lt;/code&gt;, трябва да имплементират новия метод &lt;code class=&quot;language-text&quot;&gt;loadCombinations()&lt;/code&gt; вместо стария &lt;code class=&quot;language-text&quot;&gt;load()&lt;/code&gt;.&lt;/p&gt;
&lt;h3&gt;Необходимо действие&lt;/h3&gt;
&lt;p&gt;Преименувай или refactor-ни имплементациите на метода, за да съответстват на новото име и сигнатура.&lt;/p&gt;
&lt;h3&gt;Пример&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;Наблюдения&lt;/h3&gt;
&lt;p&gt;Тази промяна подобрява яснотата, защото името на метода е по-описателно. Възможно е да има и допълнителни параметри или return types, така че прегледът на сигнатурата е задължителен.&lt;/p&gt;
&lt;h2&gt;4. Ъпгрейд на Symfony Framework до версия 6&lt;/h2&gt;
&lt;h3&gt;Преглед на промяната&lt;/h3&gt;
&lt;p&gt;Shopware е обновил Symfony компонентите си до версия 6.&lt;/p&gt;
&lt;h3&gt;Влияние&lt;/h3&gt;
&lt;p&gt;Ъпгрейдът въвежда някои breaking changes заради deprecated възможности и промени в method signatures. Custom код, който разчита на по-стари Symfony възможности, може да се счупи или да започне да дава warnings.&lt;/p&gt;
&lt;h3&gt;Необходимо действие&lt;/h3&gt;
&lt;p&gt;Прегледай кода си за deprecated Symfony възможности и ги обнови така, че да са съвместими със Symfony 6.&lt;/p&gt;
&lt;h3&gt;Наблюдения&lt;/h3&gt;
&lt;p&gt;Поддържането на актуална Symfony версия носи по-добра производителност, сигурност и достъп до нови възможности. Но изисква внимателен code review и тестване, за да се гарантира съвместимост.&lt;/p&gt;
&lt;h2&gt;5. Промени при Stock Handling&lt;/h2&gt;
&lt;h3&gt;Преглед на промяната&lt;/h3&gt;
&lt;p&gt;Въведен е нов Stock API, достъпен зад feature flag-а &lt;code class=&quot;language-text&quot;&gt;STOCK_HANDLING&lt;/code&gt;.&lt;/p&gt;
&lt;h3&gt;Влияние&lt;/h3&gt;
&lt;p&gt;Класове и услуги, свързани с управлението на наличности, може да трябва да се адаптират към новата API структура, особено ако взаимодействат директно със stock data.&lt;/p&gt;
&lt;h3&gt;Необходимо действие&lt;/h3&gt;
&lt;p&gt;Използвай новите stock handling методи, предоставени от API-то, и се увери, че всяка stock-related логика отговаря на обновената структура.&lt;/p&gt;
&lt;h3&gt;Пример&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;Наблюдения&lt;/h3&gt;
&lt;p&gt;Новият Stock API дава по-устойчив и гъвкав начин за управление на наличности, което потенциално опростява customizations и интеграции с външни системи.&lt;/p&gt;
&lt;h2&gt;6. Ъпгрейд на Storefront към Bootstrap&lt;/h2&gt;
&lt;h3&gt;Преглед на промяната&lt;/h3&gt;
&lt;p&gt;Storefront е обновен от Bootstrap 4 към Bootstrap 5, а jQuery е премахнат като dependency.&lt;/p&gt;
&lt;h3&gt;Влияние&lt;/h3&gt;
&lt;p&gt;Custom JavaScript код и templates, които разчитат на jQuery или Bootstrap 4 компоненти, трябва да бъдат refactor-нати, за да съответстват на Bootstrap 5 и да използват native JavaScript, където е необходимо.&lt;/p&gt;
&lt;h3&gt;Необходимо действие&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Замени употребата на jQuery с native JavaScript или Bootstrap 5 utilities.&lt;/li&gt;
&lt;li&gt;Обнови Bootstrap класове и компоненти, за да съответстват на naming-а и структурата в Bootstrap 5.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Наблюдения&lt;/h3&gt;
&lt;p&gt;Bootstrap 5 носи подобрения в производителността, по-малко dependencies и модернизирани компоненти. Обновяването може да отнеме време, но дава дългосрочни ползи за поддръжката и user experience-а.&lt;/p&gt;
&lt;h2&gt;7. Промени в data attribute-а за Offcanvas Cart (Shopware 6.6)&lt;/h2&gt;
&lt;h3&gt;Преглед на промяната&lt;/h3&gt;
&lt;p&gt;В Shopware 6.6 има фина, но важна промяна в data attribute-а, използван за trigger-ване на offcanvas cart функционалността.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Предишен data attribute:&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;Нов data attribute:&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;Влияние&lt;/h3&gt;
&lt;p&gt;Custom templates или themes, които използват attribute-а &lt;code class=&quot;language-text&quot;&gt;data-offcanvas-cart&lt;/code&gt; без тирета, може да установят, че offcanvas cart вече не работи както се очаква, защото JavaScript listener-ът в Shopware 6.6 търси версията с тирета.&lt;/p&gt;
&lt;h3&gt;Необходимо действие&lt;/h3&gt;
&lt;p&gt;Обнови attribute-а &lt;code class=&quot;language-text&quot;&gt;data-offcanvas-cart&lt;/code&gt; в templates към &lt;code class=&quot;language-text&quot;&gt;data-off-canvas-cart&lt;/code&gt;.&lt;/p&gt;
&lt;h3&gt;Пример&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;Наблюдения&lt;/h3&gt;
&lt;p&gt;Тази промяна не е добре документирана в официалните release notes на Shopware 6.6, но е критична за правилното функциониране на offcanvas cart. JavaScript-ът, отговорен за инициализирането на cart функционалността, разчита на attribute-а &lt;code class=&quot;language-text&quot;&gt;data-off-canvas-cart&lt;/code&gt;, и всяко отклонение може да попречи на cart-а да работи.&lt;/p&gt;
&lt;h3&gt;Допълнителни бележки&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Консистентността е ключова:&lt;/strong&gt; Увери се, че всички места, където се използва offcanvas cart attribute-ът, са обновени.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Тествай старателно:&lt;/strong&gt; След промяната тествай cart функционалността, за да потвърдиш, че работи както се очаква.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Провери за подобни промени:&lt;/strong&gt; Други data attributes или event listeners може да са минали през сходни промени; прегледай custom templates съответно.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;8. Промени в CSRF защитата&lt;/h2&gt;
&lt;h3&gt;Преглед на промяната&lt;/h3&gt;
&lt;p&gt;Shopware 6.5 и по-новите версии премахват explicit CSRF token handling в templates, преминавайки към SameSite cookie стратегия за CSRF защита.&lt;/p&gt;
&lt;h3&gt;Влияние&lt;/h3&gt;
&lt;p&gt;Templates и forms, които преди са включвали CSRF tokens чрез функцията &lt;code class=&quot;language-text&quot;&gt;sw_csrf&lt;/code&gt;, ще срещнат грешки, защото тази функция вече не съществува.&lt;/p&gt;
&lt;h3&gt;Необходимо действие&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Премахни CSRF Token Functions:&lt;/strong&gt; Премахни употребата на &lt;code class=&quot;language-text&quot;&gt;{{ sw_csrf(&apos;route_name&apos;) }}&lt;/code&gt; от templates.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Разчитай на SameSite Cookies:&lt;/strong&gt; Довери се на вградената SameSite cookie стратегия за CSRF защита, която не изисква explicit tokens във forms.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Адаптирай form attributes:&lt;/strong&gt; Увери се, че forms и AJAX requests са конфигурирани правилно, за да работят с новия механизъм за CSRF защита.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Пример&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;Наблюдения&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Разрешаване на грешката:&lt;/strong&gt; Премахването на функцията &lt;code class=&quot;language-text&quot;&gt;sw_csrf&lt;/code&gt; разрешава грешката &quot;Unknown &apos;sw_csrf&apos; function&quot;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Запазване на сигурността:&lt;/strong&gt; SameSite cookie стратегията продължава да предпазва от CSRF атаки без допълнителни tokens.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Опростени templates:&lt;/strong&gt; Forms стават по-чисти и малко по-прости без нуждата от CSRF tokens.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Допълнителни бележки&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Тестването е критично:&lt;/strong&gt; След тези промени тествай старателно form submissions, за да се увериш, че работят правилно.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Разбери новия механизъм:&lt;/strong&gt; Запознай се с начина, по който работи SameSite cookie стратегията, за да поддържаш сигурно приложение.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Обнови документацията:&lt;/strong&gt; Увери се, че всяка вътрешна документация отразява тази промяна, за да предотврати бъдещо объркване.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;9. Работа с проблеми при Offcanvas Cart&lt;/h2&gt;
&lt;h3&gt;Преглед на сценария&lt;/h3&gt;
&lt;p&gt;След обновяване на templates и премахване на функцията &lt;code class=&quot;language-text&quot;&gt;sw_csrf&lt;/code&gt;, разработчиците може все още да срещат проблеми, при които натискането на бутона &quot;Add to Cart&quot; отваря offcanvas cart, но той изглежда празен.&lt;/p&gt;
&lt;h3&gt;Root Cause&lt;/h3&gt;
&lt;p&gt;Offcanvas cart може да не показва добавените items заради липсващи или неправилни параметри във form submission-а, по-конкретно липсата на input field-а &lt;code class=&quot;language-text&quot;&gt;redirectTo&lt;/code&gt;.&lt;/p&gt;
&lt;h3&gt;Необходимо действие&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Добави параметъра &lt;code class=&quot;language-text&quot;&gt;redirectTo&lt;/code&gt;:&lt;/strong&gt; Включи hidden input field с име &lt;code class=&quot;language-text&quot;&gt;redirectTo&lt;/code&gt; и стойност &lt;code class=&quot;language-text&quot;&gt;frontend.cart.offcanvas&lt;/code&gt; във формите за add-to-cart.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Осигури правилни data attributes:&lt;/strong&gt; Провери дали всички нужни data attributes присъстват и са именувани правилно.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Пример&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;Наблюдения&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Възстановяване на функционалността:&lt;/strong&gt; Добавянето на параметъра &lt;code class=&quot;language-text&quot;&gt;redirectTo&lt;/code&gt; казва на Shopware да зареди offcanvas cart при добавяне на item, така че cart-ът да се показва правилно.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Внимание към детайла:&lt;/strong&gt; Малки пропуски като липсващи input fields могат да доведат до значителни функционални проблеми, което подчертава нуждата от внимателен code review.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Допълнителни бележки&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Консистентност в data attributes:&lt;/strong&gt; Провери още веднъж дали data attributes като &lt;code class=&quot;language-text&quot;&gt;data-product-id&lt;/code&gt; са зададени правилно.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Преглед на JavaScript dependencies:&lt;/strong&gt; Увери се, че всички JavaScript plugins или components, свързани с cart-а, са правилно заредени и инициализирани.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Изчисти cache-а:&lt;/strong&gt; След промените изчисти Shopware cache-а и browser cache-а, за да избегнеш проблеми от остарели файлове.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;10. Подобрения в Rule Builder&lt;/h2&gt;
&lt;h3&gt;Преглед на промяната&lt;/h3&gt;
&lt;p&gt;Rule Builder API е разширено, за да поддържа по-сложна conditional logic.&lt;/p&gt;
&lt;h3&gt;Влияние&lt;/h3&gt;
&lt;p&gt;Custom rules и conditions може да се нуждаят от корекции, за да съответстват на новите interfaces или methods, предоставени от подобрения Rule Builder.&lt;/p&gt;
&lt;h3&gt;Необходимо действие&lt;/h3&gt;
&lt;p&gt;Прегледай документацията на Rule Builder и обнови custom rule implementations, за да гарантираш съвместимост.&lt;/p&gt;
&lt;h3&gt;Наблюдения&lt;/h3&gt;
&lt;p&gt;Разширените rule capabilities позволяват по-прецизно targeting и customization в Shopware. Използването на тези нови възможности може да доведе до по-добра адаптивност и по-персонализирани преживявания за крайните потребители.&lt;/p&gt;
&lt;h2&gt;Заключение&lt;/h2&gt;
&lt;p&gt;Shopware 6.5 и 6.6 въвеждат няколко важни промени в класове, namespace-и, data attributes и механизми за сигурност, които разработчиците трябва да адресират, за да запазят съвместимост и да се възползват от новите възможности. Обновяването на codebase-а изисква внимателен преглед и тестване, но отваря възможности за подобрение на производителността, сигурността и функционалността.&lt;/p&gt;
&lt;h2&gt;Препоръки&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Планирай предварително:&lt;/strong&gt; Преди обновяване прегледай официалните release notes и upgrade guides на Shopware за изчерпателна информация.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Тествай старателно:&lt;/strong&gt; Имплементирай промените в staging environment и направи обстойно тестване, за да откриеш и поправиш проблеми.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Използвай документацията:&lt;/strong&gt; Използвай документацията на Shopware и community forums за насоки по конкретни промени.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Остани информиран:&lt;/strong&gt; Следи бъдещите updates, за да предвиждаш предстоящи промени и да се подготвяш навреме.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Допълнителни наблюдения&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Внимание към детайла:&lt;/strong&gt; Малки промени, като тирета в data attributes или премахване на функции, могат да имат значително влияние. Винаги преглеждай template updates внимателно.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Community Support:&lt;/strong&gt; Shopware community-то е активно и collaborative. Взаимодействието с други разработчици може да даде insights и solutions за общи предизвикателства.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Best Practices:&lt;/strong&gt; Приемането на обновени best practices, като използване на native JavaScript вместо jQuery и разчитане на модерни security strategies, води до по-чист и по-ефективен код.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Monitoring Deprecations:&lt;/strong&gt; Следенето на deprecation notices и подготовката за бъдещи removals може да минимизира disruptions по време на upgrades.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;Като разберат и адресират промените в класове, namespace-и, data attributes и сигурност в Shopware 6.5 и 6.6, разработчиците могат да осигурят плавен преход и да продължат да изграждат устойчиви, future-proof e-commerce решения.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Еволюцията на Docker Compose: какво се промени и защо има значение]]></title><description><![CDATA[TL;DR: Docker Compose v1 () беше напълно премахнат през април 2025. Полето  в твоя YAML е мъртво. Ключът  вече е просто . Watch mode е готов…]]></description><link>https://bdteo.com/bg/docker-compose-major-changes-since-october-2023/</link><guid isPermaLink="false">https://bdteo.com/bg/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;) беше напълно премахнат през април 2025. Полето &lt;code class=&quot;language-text&quot;&gt;version&lt;/code&gt; в твоя YAML е мъртво. Ключът &lt;code class=&quot;language-text&quot;&gt;x-develop&lt;/code&gt; вече е просто &lt;code class=&quot;language-text&quot;&gt;develop&lt;/code&gt;. Watch mode е готов за продукционна употреба с &lt;code class=&quot;language-text&quot;&gt;initial_sync&lt;/code&gt;. Има критичен path traversal CVE (CVE-2025-62725), ако използваш &lt;code class=&quot;language-text&quot;&gt;include&lt;/code&gt; с OCI artifacts -- обнови до v2.40.2+. И да, Compose скочи от v2 на v5. Подробностите са по-долу.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Първоначално публикувано през ноември 2024. Обновено през март 2026 с Compose v5, CVE-2025-62725, премахването на v1 и нови възможности в спецификацията.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Ако използваш Docker Compose от известно време, вероятно си забелязал как неща се чупят или се променят под краката ти. Последните две години бяха най-агресивната еволюция, през която Compose някога е минавал -- и не всичко беше очевидно.&lt;/p&gt;
&lt;p&gt;Използвам Compose всеки ден. Повечето ми &lt;a href=&quot;/laravel-sail-vs-laradock-choosing-right-docker-solution/&quot;&gt;development setups&lt;/a&gt; вървят върху него. Когато нещата се променят, забелязвам. Ето какво реално има значение.&lt;/p&gt;
&lt;h2&gt;Какво се счупи&lt;/h2&gt;
&lt;h3&gt;docker-compose е мъртъв&lt;/h3&gt;
&lt;p&gt;Не deprecated. Не в maintenance mode. &lt;strong&gt;Мъртъв.&lt;/strong&gt; Самостоятелният &lt;code class=&quot;language-text&quot;&gt;docker-compose&lt;/code&gt; binary (Python-базираният v1) беше премахнат от GitHub Actions runners и официалните Docker images през април 2025 &lt;small&gt;&lt;a href=&quot;#ref1&quot;&gt;[1]&lt;/a&gt;&lt;/small&gt;. Ако твоите CI/CD pipelines все още сочат към &lt;code class=&quot;language-text&quot;&gt;docker-compose&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 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;Go-базираният &lt;code class=&quot;language-text&quot;&gt;docker compose&lt;/code&gt; (v2, сега v5) е реалната имплементация от 2022 насам. v1 CLI беше на животоподдържаща система заради съвместимост. Тази система беше изключена.&lt;/p&gt;
&lt;h3&gt;Полето version го няма&lt;/h3&gt;
&lt;p&gt;Спри да слагаш &lt;code class=&quot;language-text&quot;&gt;version: &quot;3.8&quot;&lt;/code&gt; най-горе в Compose файловете си. Не прави нищо. Игнорира се още от v2 и вече е официално deprecated. Модерните Compose файлове започват със &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;Ако видиш &lt;code class=&quot;language-text&quot;&gt;version:&lt;/code&gt; в tutorial, този tutorial е остарял.&lt;/p&gt;
&lt;h3&gt;Други 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; -- използвай Docker networks. Links са legacy още от пускането на Compose v2.&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; -- остави Docker да управлява имената. Hardcoded имена чупят scaling-а и причиняват конфликти.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Кратък volume syntax за complex mounts&lt;/strong&gt; -- използвай long-form syntax с &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;Какво е ново и реално полезно&lt;/h2&gt;
&lt;h3&gt;Watch Mode (вече готов за продукционна употреба)&lt;/h3&gt;
&lt;p&gt;Това е най-голямото quality-of-life подобрение от години. Секцията &lt;code class=&quot;language-text&quot;&gt;develop&lt;/code&gt; (преди &lt;code class=&quot;language-text&quot;&gt;x-develop&lt;/code&gt; -- махни &lt;code class=&quot;language-text&quot;&gt;x-&lt;/code&gt; prefix-а, вече не е experimental) ти позволява да дефинираш file watch правила, които автоматично sync-ват или rebuild-ват при промяна на файлове:&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;Има три налични actions (от 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; -- копира променените файлове в container-а без rebuild&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code class=&quot;language-text&quot;&gt;restart&lt;/code&gt;&lt;/strong&gt; -- рестартира service-а, когато файловете се променят&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code class=&quot;language-text&quot;&gt;rebuild&lt;/code&gt;&lt;/strong&gt; -- стартира пълен rebuild&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Към септември 2025 има и &lt;strong&gt;&lt;code class=&quot;language-text&quot;&gt;initial_sync&lt;/code&gt;&lt;/strong&gt; -- той sync-ва всички файлове веднага щом стартираш &lt;code class=&quot;language-text&quot;&gt;docker compose watch&lt;/code&gt;, така че не чакаш първата промяна да задейства synchronization. Това беше болезнена точка дълго време.&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;Край на ръчните rebuild-и по време на development. Това наистина промени workflow-а ми.&lt;/p&gt;
&lt;h3&gt;Include с OCI Artifacts&lt;/h3&gt;
&lt;p&gt;Директивата &lt;code class=&quot;language-text&quot;&gt;include&lt;/code&gt; вече може да дърпа Compose fragments от OCI registries:&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;Има и experimental поддръжка за Git repositories. Това е полезно за споделяне на общи service definitions между проекти -- database configs, monitoring stacks и т.н.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Но първо прочети security секцията по-долу.&lt;/strong&gt; Има CVE.&lt;/p&gt;
&lt;h3&gt;GPU Support&lt;/h3&gt;
&lt;p&gt;GPU passthrough стана по-чист. Вече има по-кратък &lt;code class=&quot;language-text&quot;&gt;gpus:&lt;/code&gt; syntax (v2.30.0+) заедно с verbose подхода през &lt;code class=&quot;language-text&quot;&gt;deploy.resources.reservations.devices&lt;/code&gt;. AMD GPU support беше официално интегриран през 2025 -- вече не е само NVIDIA.&lt;/p&gt;
&lt;h3&gt;Models Element&lt;/h3&gt;
&lt;p&gt;Compose spec вече включва &lt;code class=&quot;language-text&quot;&gt;models&lt;/code&gt; element за дефиниране на AI/ML models като OCI artifacts. Можеш да пакетираш LLMs и inference runtimes директно в Compose setup-а си. Нишово, но интересно, ако правиш local AI work.&lt;/p&gt;
&lt;h3&gt;По-добри зависимости&lt;/h3&gt;
&lt;p&gt;Условията на &lt;code class=&quot;language-text&quot;&gt;depends_on&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 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;Опциите &lt;code class=&quot;language-text&quot;&gt;restart: true&lt;/code&gt; и &lt;code class=&quot;language-text&quot;&gt;required: false&lt;/code&gt; са истински полезни за устойчиви local development setups.&lt;/p&gt;
&lt;h2&gt;Какво трябва да знаеш&lt;/h2&gt;
&lt;h3&gt;CVE-2025-62725: Include Path Traversal&lt;/h3&gt;
&lt;p&gt;Ако използваш директивата &lt;code class=&quot;language-text&quot;&gt;include&lt;/code&gt; с OCI artifacts, &lt;strong&gt;обнови до v2.40.2 или по-нова версия веднага&lt;/strong&gt; &lt;small&gt;&lt;a href=&quot;#ref2&quot;&gt;[2]&lt;/a&gt;&lt;/small&gt;. Path traversal vulnerability позволява на attacker да избяга от cache directory по време на artifact resolution. Дори невинно изглеждащо &lt;code class=&quot;language-text&quot;&gt;docker compose ps&lt;/code&gt; може да го trigger-не, ако твоят Compose file include-ва malicious OCI reference.&lt;/p&gt;
&lt;p&gt;Docker patch-на това с &lt;code class=&quot;language-text&quot;&gt;validatePathInBase()&lt;/code&gt; check, но трябва да си на fixed version.&lt;/p&gt;
&lt;h3&gt;Compose v5&lt;/h3&gt;
&lt;p&gt;Docker скочи от v2 на v5 (прескачайки 3 и 4, за да избегне объркване със старите file format versions) &lt;small&gt;&lt;a href=&quot;#ref3&quot;&gt;[3]&lt;/a&gt;&lt;/small&gt;. Функционално v5 е същият като късните v2 releases, но включва официален &lt;strong&gt;Go SDK&lt;/strong&gt; за programmatic access -- което значи, че можеш да embed-неш Compose functionality директно в Go applications, без да shell-ваш към CLI.&lt;/p&gt;
&lt;p&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;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 е default build tool&lt;/h3&gt;
&lt;p&gt;Docker Bake (през BuildKit) вече е default за &lt;code class=&quot;language-text&quot;&gt;docker compose build&lt;/code&gt;. Той се справя с multi-target builds, cross-platform compilation и advanced caching strategies по-добре от legacy builder-а. Ако още не си поглеждал &lt;code class=&quot;language-text&quot;&gt;docker-bake.hcl&lt;/code&gt; файлове, струва си да ги разбереш -- особено за сложни multi-service builds.&lt;/p&gt;
&lt;h3&gt;Healthcheck подобрения&lt;/h3&gt;
&lt;p&gt;Полето &lt;code class=&quot;language-text&quot;&gt;start_interval&lt;/code&gt; ти позволява да зададеш по-бърз check interval по време на startup grace period:&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;Това означава, че dependent services стартират по-бързо, без да компрометираш production health check intervals.&lt;/p&gt;
&lt;h2&gt;Migration Checklist&lt;/h2&gt;
&lt;p&gt;Ако не си обновявал Compose setup-а си от известно време:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Премахни &lt;code class=&quot;language-text&quot;&gt;version:&lt;/code&gt;&lt;/strong&gt; от всички Compose files.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Замени &lt;code class=&quot;language-text&quot;&gt;docker-compose&lt;/code&gt;&lt;/strong&gt; с &lt;code class=&quot;language-text&quot;&gt;docker compose&lt;/code&gt; във всички scripts и CI configs.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Преименувай &lt;code class=&quot;language-text&quot;&gt;x-develop&lt;/code&gt;&lt;/strong&gt; на &lt;code class=&quot;language-text&quot;&gt;develop&lt;/code&gt;** в watch configurations.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Обнови до v2.40.2+&lt;/strong&gt;, ако използваш &lt;code class=&quot;language-text&quot;&gt;include&lt;/code&gt; (CVE-2025-62725).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Замени &lt;code class=&quot;language-text&quot;&gt;links&lt;/code&gt;&lt;/strong&gt; с Docker networks, ако някак все още ги използваш.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Тествай CI-то си&lt;/strong&gt; -- GitHub Actions обновиха runners до Compose v2.40.3 през февруари 2026 &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;References&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 announcement за премахването на 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;Подробният writeup на Imperva за include path traversal vulnerability.&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;Официална release история, включително 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;GitHub update-ът за runners през февруари 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;Официална Compose file reference.&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;Документацията на Docker за watch mode.&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 docs, включително AMD support.&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 directive с OCI и Git support.&lt;/em&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h3&gt;Related Posts&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: избор на правилното Docker решение&lt;/a&gt; -- сравнение на Docker-базирани PHP development environments.&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[Отключете силата на 'git grep' за ефективно търсене в код]]></title><description><![CDATA[В обширно кралство, пълно с безброй свитъци и ръкописи, живеел учен на име Аларик. Библиотеката му била огромна — лабиринт от знание, в…]]></description><link>https://bdteo.com/bg/unlocking-the-power-of-git-grep/</link><guid isPermaLink="false">https://bdteo.com/bg/unlocking-the-power-of-git-grep/</guid><pubDate>Wed, 13 Nov 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;В обширно кралство, пълно с безброй свитъци и ръкописи, живеел учен на име Аларик. Библиотеката му била огромна — лабиринт от знание, в който древни текстове се смесвали със съвременни писания, а тайни се криели между редовете. Аларик често се оказвал в търсене на една-единствена изплъзваща се фраза сред това море от информация — задача, която с всеки изминал ден ставала все по-плашеща.&lt;/p&gt;
&lt;p&gt;Една сутрин, докато слънцето хвърляло златни лъчи върху прашните томове, Аларик тръгнал да открие конкретна идея, спомената в архивите му, известна само като „Шепнещия сигил“. Той преглеждал том след том, използвайки обичайните си методи за пресяване на страниците — методи, които вече му се стрували мудни и неточни. Колкото по-дълбоко навлизал, толкова повече се оплитал в нерелевантни пасажи, дубликати и подвеждащи препратки. Раздразнението растяло, докато часовете се превръщали в дни, а напредъкът оставал нищожен.&lt;/p&gt;
&lt;p&gt;Тогава при Аларик дошъл стар мъдрец и забелязал мъката му. С усмивка на човек, който знае, мъдрецът казал: „Може би търсиш по трудния начин. Има скрит път, познат само на онези, които подреждат знанието си разумно.“ Заинтригуван, Аларик слушал как мъдрецът обяснява метод, който стеснявал търсенето му, прорязвал шума и го водел направо към текстовете, които търсел.&lt;/p&gt;
&lt;p&gt;Въоръжен с този нов подход, Аларик опитал пак. Този път нерелевантният шум избледнял. Пътят към „Шепнещия сигил“ станал ясен и той намерил търсеното с удивителна скорост. Сякаш беше отключил тайна порта в лабиринта си, която му дала бърз достъп до точното знание, от което се нуждаел.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Пуф!&lt;/strong&gt; Тайната била разкрита: силата на &lt;code class=&quot;language-text&quot;&gt;git grep&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;Какво всъщност е &lt;code class=&quot;language-text&quot;&gt;git grep&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;Обикновеният &lt;code class=&quot;language-text&quot;&gt;grep -r&lt;/code&gt; обхожда файловата система. Той прилежно чете всичко по пътя си: source code, log files, build outputs, онзи случаен dump файл от 4 MB, който колегата ти е забравил да изтрие, цялото дърво &lt;code class=&quot;language-text&quot;&gt;node_modules&lt;/code&gt;. &lt;code class=&quot;language-text&quot;&gt;git grep&lt;/code&gt; прави нещо по-тясно: търси във файловете, за които Git вече знае. Точно този избор в дизайна е мястото, откъдето идва по-голямата част от стойността му.&lt;/p&gt;
&lt;h3&gt;В какво е добър &lt;code class=&quot;language-text&quot;&gt;git grep&lt;/code&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Търси tracked файлове, не файловата система.&lt;/strong&gt; Git пази списък на всеки файл, който някога си staged или committed — index-а. &lt;code class=&quot;language-text&quot;&gt;git grep&lt;/code&gt; чете от този списък. Untracked боклукът просто го няма там. Няма &lt;code class=&quot;language-text&quot;&gt;node_modules/&lt;/code&gt;, няма &lt;code class=&quot;language-text&quot;&gt;dist/&lt;/code&gt;, няма coverage reports, няма случаен log файл — защото Git никога не е бил уведомен за тях.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;По-бърз е от &lt;code class=&quot;language-text&quot;&gt;grep -r&lt;/code&gt; в големи repos.&lt;/strong&gt; Той вече има списъка с файлове, така че пропуска обхождането на файловата система. Пуска няколко threads паралелно. Печалбата е реална, но не е магия. &lt;code class=&quot;language-text&quot;&gt;git grep&lt;/code&gt; обхожда същите blobs, които би обходил &lt;code class=&quot;language-text&quot;&gt;grep&lt;/code&gt;, просто с по-малко церемония. Няма content search index — &quot;Git index&quot; е списък от файлови пътища и blob hashes, не Lucene-стил inverted index.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Може да търси във всеки ref без checkout.&lt;/strong&gt; Това е killer feature-ът. Tag, branch, commit, tree object — насочи &lt;code class=&quot;language-text&quot;&gt;git grep&lt;/code&gt; директно към него. Без &lt;code class=&quot;language-text&quot;&gt;git checkout&lt;/code&gt;, без stash танц, без отбивка от това, което правиш в момента.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Практически примери&lt;/h3&gt;
&lt;h4&gt;Основно търсене&lt;/h4&gt;
&lt;p&gt;За да търсиш конкретен термин, например &lt;code class=&quot;language-text&quot;&gt;&quot;initializeSettings&quot;&lt;/code&gt;, в repository-то си:&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;Това сканира всички tracked файлове в текущия branch за точно съвпадение.&lt;/p&gt;
&lt;h4&gt;Търсене без значение на главни и малки букви&lt;/h4&gt;
&lt;p&gt;За case-insensitive търсене, което е полезно, когато не си сигурен в capitalization-а:&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;Това ще намери съвпадения независимо от разликите в главни и малки букви.&lt;/p&gt;
&lt;h4&gt;Търсене в конкретен branch&lt;/h4&gt;
&lt;p&gt;За да търсиш в друг branch, без да превключваш към него, например във &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;Това е ходът, който трудно се бие. Няма checkout, няма stash, само отговорът.&lt;/p&gt;
&lt;h4&gt;Търсене във всички branches&lt;/h4&gt;
&lt;p&gt;За да търсиш термин във всеки branch, включително 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;За да търсиш във всеки commit, за който Git някога е чувал, не само в tip-of-branch:&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;Това намира съвпадения във всеки blob навсякъде в историята ти. В натоварено repo може да отнеме момент — буквално обхожда всеки commit.&lt;/p&gt;
&lt;h4&gt;Търсене в commit историята&lt;/h4&gt;
&lt;p&gt;За да намериш кога конкретен string е бил добавен или премахнат, използвай:&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;Това показва commits, които са въвели или премахнали термина &lt;code class=&quot;language-text&quot;&gt;&quot;optimizePerformance&quot;&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;За да видиш реалните diffs, където терминът е бил добавен или премахнат:&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;Използване на regular expressions&lt;/h4&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;git grep&lt;/code&gt; поддържа regular expressions за по-напреднали търсения:&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;Това съвпада с Python function definitions: &lt;code class=&quot;language-text&quot;&gt;def&lt;/code&gt;, whitespace, име на function, после буквална отваряща скоба. (В extended regex &lt;code class=&quot;language-text&quot;&gt;\(&lt;/code&gt; е буквална скоба, а &lt;code class=&quot;language-text&quot;&gt;(&lt;/code&gt; би означавала group, затова backslash-ът е там.)&lt;/p&gt;
&lt;h3&gt;Какво &lt;code class=&quot;language-text&quot;&gt;git grep&lt;/code&gt; чете и какво не чете&lt;/h3&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;git grep&lt;/code&gt; обхожда index-а. Това е. Той не parse-ва &lt;code class=&quot;language-text&quot;&gt;.gitignore&lt;/code&gt;. Много хора, включително предишна версия на тази статия, твърдят, че го прави — и твърдението е почти вярно по начина, по който „Земята е плоска“ е почти вярно, ако цял живот гледаш само един паркинг.&lt;/p&gt;
&lt;p&gt;Двете неща съвпадат само защото gitignored файловете обикновено са и untracked. В момента, в който един файл е &lt;em&gt;едновременно&lt;/em&gt; gitignored &lt;em&gt;и&lt;/em&gt; tracked — някой е пуснал &lt;code class=&quot;language-text&quot;&gt;git add -f&lt;/code&gt;, или файлът е бил committed преди правилото да съществува — &lt;code class=&quot;language-text&quot;&gt;git grep&lt;/code&gt; с радост ще го претърси. &lt;code class=&quot;language-text&quot;&gt;rg&lt;/code&gt; няма.&lt;/p&gt;
&lt;p&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;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;Така че точният израз е: &lt;code class=&quot;language-text&quot;&gt;git grep&lt;/code&gt; търси tracked файлове. Това случайно пропуска &lt;em&gt;повечето&lt;/em&gt; от онова, което &lt;code class=&quot;language-text&quot;&gt;.gitignore&lt;/code&gt; би пропуснал, но механизмът е различен и edge case-ът има значение — особено когато ловиш string, който накрая се оказва в generated файл, force-added от някого преди години.&lt;/p&gt;
&lt;p&gt;Механизмът на &lt;code class=&quot;language-text&quot;&gt;.gitignore&lt;/code&gt; влиза в &lt;code class=&quot;language-text&quot;&gt;git grep&lt;/code&gt; само през два opt-in режима:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;--untracked&lt;/code&gt; — търси и untracked файлове. &lt;em&gt;В този режим&lt;/em&gt; &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; по подразбиране и пропуска ignored файлове (override с &lt;code class=&quot;language-text&quot;&gt;--no-exclude-standard&lt;/code&gt;, за да търсиш и в тях).&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;--no-index&lt;/code&gt; — търси в текущата директория, като игнорира Git изцяло. Полезно вътре в repo, когато искаш plain-grep семантика. В този режим &lt;code class=&quot;language-text&quot;&gt;git grep&lt;/code&gt; &lt;em&gt;не&lt;/em&gt; се консултира с &lt;code class=&quot;language-text&quot;&gt;.gitignore&lt;/code&gt; по подразбиране — opt in с &lt;code class=&quot;language-text&quot;&gt;--exclude-standard&lt;/code&gt;, ако го искаш.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Default &lt;code class=&quot;language-text&quot;&gt;git grep&lt;/code&gt;, без flags, никога не отваря твоя &lt;code class=&quot;language-text&quot;&gt;.gitignore&lt;/code&gt; файл.&lt;/p&gt;
&lt;h2&gt;Кога да посегнеш към &lt;code class=&quot;language-text&quot;&gt;rg&lt;/code&gt; вместо това&lt;/h2&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;git grep&lt;/code&gt; и &lt;code class=&quot;language-text&quot;&gt;rg&lt;/code&gt; (ripgrep) не са истински конкуренти. Те обхождат различни неща, а сериозният toolbox има и двете.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;git grep&lt;/code&gt; обхожда &lt;strong&gt;index-а&lt;/strong&gt;: tracked файлове, плюс всеки ref или tree object, към който го насочиш.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;rg&lt;/code&gt; обхожда &lt;strong&gt;файловата система&lt;/strong&gt;: всеки файл под текущата директория, минус онова, което твоите &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; и global excludes му казват да пропусне.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Всяко от тях прави нещо, което другото не може.&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;git grep&lt;/code&gt; печели, когато искаш да търсиш из историята без checkout:&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; печели, когато всъщност искаш семантиката на файловата система с правилно gitignore поведение — включително току-що клонирана subfolder, която още не си &lt;code class=&quot;language-text&quot;&gt;git add&lt;/code&gt;-нал, generated файлове, за които Git никога не е чувал, или директория, която изобщо не е Git repo:&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; е и engine-ът зад project search-а на VS Code, затова &quot;Find in Files&quot; се усеща точно като да пуснеш &lt;code class=&quot;language-text&quot;&gt;rg&lt;/code&gt; в terminal. Има солидна Unicode поддръжка, а върху повечето modern corpora е поне толкова бърз, колкото &lt;code class=&quot;language-text&quot;&gt;git grep&lt;/code&gt;, и често по-бърз — &lt;a href=&quot;https://github.com/BurntSushi/ripgrep/blob/master/README.md&quot;&gt;Linux kernel benchmark-ът в README-то на ripgrep&lt;/a&gt; показва, че ripgrep бие &lt;code class=&quot;language-text&quot;&gt;git grep -P&lt;/code&gt; с около 3x на същия query. (Съвет: ако искаш поведението &quot;case-sensitive само когато pattern-ът ти има uppercase&quot;, подай &lt;code class=&quot;language-text&quot;&gt;-S&lt;/code&gt; за smart-case — това е opt-in, не default.)&lt;/p&gt;
&lt;p&gt;Ако още нямаш &lt;code class=&quot;language-text&quot;&gt;rg&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;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;Сложи &lt;code class=&quot;language-text&quot;&gt;rg&lt;/code&gt; до &lt;code class=&quot;language-text&quot;&gt;git grep&lt;/code&gt; в toolbox-а си. Покриват различни задачи.&lt;/p&gt;
&lt;h3&gt;Ползи от &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;Релевантност.&lt;/strong&gt; Той търси само това, което tracked-ваш. Build outputs, caches и &lt;code class=&quot;language-text&quot;&gt;node_modules&lt;/code&gt; не ти се пречкат — защото Git никога не ги е видял.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Скорост в големи repos.&lt;/strong&gt; Multi-threaded, без обхождане на файловата система.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Обхват в историята.&lt;/strong&gt; Всеки branch, tag или commit, без да напускаш working tree-то си. Това е частта, която &lt;code class=&quot;language-text&quot;&gt;rg&lt;/code&gt; не може да направи.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;По-малко binary шум.&lt;/strong&gt; Като &lt;code class=&quot;language-text&quot;&gt;grep&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;git grep&lt;/code&gt; маркира binaries с &quot;Binary file X matches&quot;, вместо да излива bytes — но понеже обхожда tracked файлове, обикновено среща по-малко от тях още в началото. Подай &lt;code class=&quot;language-text&quot;&gt;-I&lt;/code&gt;, за да пропуснеш binaries изцяло.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Допълнителни съвети&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Paging на резултатите:&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;Броене на съвпаденията по файл:&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;Показване на номера на редове:&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;Отвори всяко съвпадение в editor-а си:&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;Смени &lt;code class=&quot;language-text&quot;&gt;code&lt;/code&gt; с &lt;code class=&quot;language-text&quot;&gt;nvim&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;subl&lt;/code&gt; или каквото използваш.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Заключение&lt;/h2&gt;
&lt;p&gt;Точно както Аларик намери скрит път в лабиринтната си библиотека, &lt;code class=&quot;language-text&quot;&gt;git grep&lt;/code&gt; прорязва чиста линия през tracked codebase: бърз, branch-aware и незадръстен от нещо, за което Git никога не е бил уведомен. Не е универсален заместител на &lt;code class=&quot;language-text&quot;&gt;grep&lt;/code&gt; и не е заместител на &lt;code class=&quot;language-text&quot;&gt;rg&lt;/code&gt;. Това е инструментът, който познава &lt;em&gt;index-а&lt;/em&gt; на твоето repo, и щом започнеш да посягаш към него, лабиринтът става много по-малък.&lt;/p&gt;
&lt;p&gt;Използвай &lt;code class=&quot;language-text&quot;&gt;git grep&lt;/code&gt;, когато въпросът е „къде в тази codebase, включително в историята ѝ“. Използвай &lt;code class=&quot;language-text&quot;&gt;rg&lt;/code&gt;, когато въпросът е „къде на диска, при спазване на ignore правилата ми“. В повечето дни ще искаш и двете на една ръка разстояние.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;Обновено на 2026-04-27: поправено е по-ранно твърдение, че &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; (не го прави директно), смекчено е обяснението за &quot;internal indexing&quot;, поправен е regex пример и е добавен раздел за това кога да се използва &lt;code class=&quot;language-text&quot;&gt;rg&lt;/code&gt; вместо него.&lt;/em&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[PHP 8.3.6 + IMAP на macOS с phpenv: ръководство за инсталация]]></title><description><![CDATA[Важно (обновление от 2024-11): PHP 8.4 извади ext-imap от core-а. Extension-ът се премести в PECL и на практика е deprecated -- underlying C…]]></description><link>https://bdteo.com/bg/installing-php-8-3-6-with-imap-on-macos-using-phpenv/</link><guid isPermaLink="false">https://bdteo.com/bg/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;Важно (обновление от 2024-11):&lt;/strong&gt; PHP 8.4 &lt;strong&gt;извади ext-imap&lt;/strong&gt; от core-а. Extension-ът се премести в PECL и на практика е deprecated -- underlying C library-то (&lt;code class=&quot;language-text&quot;&gt;libc-client&lt;/code&gt;) не е обновявано от 2018 насам. Ако започваш нов проект или си на PHP 8.4+, прескочи до &lt;a href=&quot;#%D0%BD%D0%B0%D0%B8%D1%81%D1%82%D0%B8%D0%BD%D0%B0-%D0%BB%D0%B8-%D1%82%D0%B8-%D1%82%D1%80%D1%8F%D0%B1%D0%B2%D0%B0-ext-imap&quot;&gt;Наистина ли ти трябва ext-imap?&lt;/a&gt; за модерни алтернативи. Ако си на PHP 8.3 или по-ранна версия и ти трябва native extension-ът, това ръководство още работи.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Бързата поправка&lt;/h2&gt;
&lt;p&gt;Ако просто искаш командите и не те интересува защо -- ето всичко подред. Трябва вече да имаш инсталирани Homebrew и phpenv.&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;Ако &lt;code class=&quot;language-text&quot;&gt;imap&lt;/code&gt; се появи в output-а, готов си. Ако не -- чети нататък.&lt;/p&gt;
&lt;h2&gt;Наистина ли ти трябва ext-imap?&lt;/h2&gt;
&lt;p&gt;Сериозен въпрос. Преди да се бориш с C libraries и compiler flags, помисли дали наистина ти трябва native IMAP extension-ът.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;PHP 8.4 махна ext-imap от core-а.&lt;/strong&gt; Той се премести в PECL, а underlying C library-то (&lt;code class=&quot;language-text&quot;&gt;libc-client&lt;/code&gt; / UW-IMAP) не е получавало update от 2018. Има thread-safety проблеми, липсващ XAUTH support и POP bugs. Няма да се върне.&lt;/p&gt;
&lt;p&gt;Модерната алтернатива е &lt;a href=&quot;https://github.com/Webklex/php-imap&quot;&gt;&lt;strong&gt;Webklex/php-imap&lt;/strong&gt;&lt;/a&gt; -- pure PHP IMAP implementation:&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;Това е. Няма brew dependencies, няма compiler flags, няма ловене на header файлове. Работи на PHP 8.0.2+ (включително 8.4 и 8.5), поддържа IMAP IDLE и OAuth, и има над 5 милиона инсталации в Packagist. Има и &lt;a href=&quot;https://github.com/Webklex/laravel-imap&quot;&gt;Laravel integration&lt;/a&gt;, ако това е твоят stack.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Използвай ext-imap само ако&lt;/strong&gt; поддържаш legacy codebase на PHP 8.3 или по-ранна версия, който вече зависи от &lt;code class=&quot;language-text&quot;&gt;imap_*&lt;/code&gt; функции, и още не можеш да мигрираш.&lt;/p&gt;
&lt;h2&gt;Какво прави всяка стъпка&lt;/h2&gt;
&lt;p&gt;Ако бързата поправка сработи, можеш да спреш да четеш. Ако не сработи -- или ако искаш да разбереш какво се случва -- ето разбивката.&lt;/p&gt;
&lt;h3&gt;Brew dependencies&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 идва с част от тези библиотеки, но phpenv има нужда от Homebrew версиите с правилните headers и pkg-config файлове. Критичната е &lt;code class=&quot;language-text&quot;&gt;imap-uw&lt;/code&gt; -- това е UW-IMAP library-то, което предоставя &lt;code class=&quot;language-text&quot;&gt;libc-client&lt;/code&gt;.&lt;/p&gt;
&lt;h3&gt;PHP_BUILD_CONFIGURE_OPTS&lt;/h3&gt;
&lt;p&gt;phpenv използва php-build отдолу, който пуска &lt;code class=&quot;language-text&quot;&gt;./configure&lt;/code&gt; върху PHP source-а. Променливата &lt;code class=&quot;language-text&quot;&gt;PHP_BUILD_CONFIGURE_OPTS&lt;/code&gt; подава flags директно към configure. Всеки &lt;code class=&quot;language-text&quot;&gt;--with-*&lt;/code&gt; flag казва на PHP къде да намери конкретна library.&lt;/p&gt;
&lt;p&gt;Най-важните за 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; -- сочи към IMAP library-то&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;--with-imap-ssl=$(brew --prefix openssl@3)&lt;/code&gt; -- включва IMAP over SSL&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;--with-kerberos=$(brew --prefix krb5)&lt;/code&gt; -- изисква се за IMAP authentication&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;CPPFLAGS поправката&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;Дори с configure flags, C preprocessor-ът понякога не може да намери header файловете. Това се случва, защото macOS не поставя Homebrew headers в default search path-а. Двата header-а, които най-често се провалят:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;openssl/ssl.h&lt;/code&gt; -- от OpenSSL&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;imap/imap.h&lt;/code&gt; -- от imap-uw&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;PATH поправката&lt;/h3&gt;
&lt;p&gt;Ако &lt;code class=&quot;language-text&quot;&gt;php -v&lt;/code&gt; показва грешната версия след инсталацията, phpenv shims не са в твоя PATH (или нещо друго ги shadow-ва). Добави това в &lt;code class=&quot;language-text&quot;&gt;~/.zshrc&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 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;После пусни &lt;code class=&quot;language-text&quot;&gt;source ~/.zshrc&lt;/code&gt; и пробвай отново.&lt;/p&gt;
&lt;h3&gt;Грешката utf8_mime2text&lt;/h3&gt;
&lt;p&gt;Ако видиш това:&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;Твоята &lt;code class=&quot;language-text&quot;&gt;imap-uw&lt;/code&gt; library е остаряла или счупена. Оправи я с:&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;После пусни инсталацията пак.&lt;/p&gt;
&lt;h2&gt;Бъдещето на PHP + IMAP&lt;/h2&gt;
&lt;p&gt;Писаното е на стената. ext-imap е deprecated, underlying C library-то е abandon-нато, а PHP 8.4 вече го извади от core-а. Ако четеш това, защото ти трябва IMAP в PHP проект, започни да планираш миграцията си към &lt;code class=&quot;language-text&quot;&gt;webklex/php-imap&lt;/code&gt;. Native extension-ът живее на заемно време.&lt;/p&gt;
&lt;p&gt;За тези от нас, които поддържат legacy codebases -- това ръководство ще продължи да работи за PHP 8.3 и по-ранни версии. Но не започвай нови проекти върху ext-imap. Няма причина да се бориш с компилация на C library, когато съществува pure PHP решение.&lt;/p&gt;
&lt;hr&gt;
&lt;h3&gt;Източници&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 документация за премахването на 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 в GitHub&lt;/a&gt; -- &lt;em&gt;Pure PHP IMAP implementation (5M+ Packagist installs).&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;Официална документация с deprecation notices.&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 за PHP extensions, включително IMAP.&lt;/em&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h3&gt;Още по темата&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: обиколка на идващите функции&lt;/a&gt; -- какво предстои в PHP.&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 и namespace updates&lt;/a&gt; -- още едно PHP migration guide за Shopware developers.&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; -- избор на правилния Docker setup за PHP development.&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[Laravel Sail срещу Laradock: сравнение за PHP Docker разработчици]]></title><description><![CDATA[TL;DR: За повечето Laravel разработчици през 2026: използвай Laravel Herd, ако искаш нулево триене (native, без Docker, настройва се за…]]></description><link>https://bdteo.com/bg/laravel-sail-vs-laradock-choosing-right-docker-solution/</link><guid isPermaLink="false">https://bdteo.com/bg/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; За повечето Laravel разработчици през 2026: използвай &lt;strong&gt;Laravel Herd&lt;/strong&gt;, ако искаш нулево триене (native, без Docker, настройва се за секунди). Използвай &lt;strong&gt;Sail&lt;/strong&gt;, ако екипът ти има нужда от идентични среди или зависиш от услуги като Redis/Meilisearch. Използвай &lt;strong&gt;Laradock&lt;/strong&gt;, ако работиш с няколко PHP framework-а. Използвай &lt;strong&gt;custom Docker Compose&lt;/strong&gt; setup, ако вече си надраснал abstraction-ите. А ако performance-ът е всичко, погледни &lt;strong&gt;FrankenPHP + Octane&lt;/strong&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Първоначално публикувано през август 2024. Обновено през март 2026 с Laravel 12/Herd/FrankenPHP и текущото състояние на ecosystem-а.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Въпросът преди беше &quot;Sail или Laradock?&quot; Това рамкиране вече е твърде тясно. Истинският въпрос е: &lt;strong&gt;как трябва да си настроя Laravel dev environment-а през 2026?&lt;/strong&gt; Вариантите са повече от всякога, а най-добрият избор зависи от това какво реално ти трябва.&lt;/p&gt;
&lt;p&gt;Използвал съм повечето от тях. В момента работя с custom Docker Compose setup, защото искам пълен контрол върху container-ите си, без abstraction-и, които скриват какво се случва. Но това е моето предпочитание, не универсална препоръка. Нека минем през това какво ти дава всяка опция.&lt;/p&gt;
&lt;h2&gt;Претендентите&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; е най-новият вариант и за много разработчици правилният. Това е native приложение (macOS и Windows -- все още няма Linux), което ти дава PHP, Nginx, Node.js и Dnsmasq без Docker. Pro версията добавя MySQL, PostgreSQL, Redis и debugging инструменти.&lt;/p&gt;
&lt;p&gt;Убийствената функция: смяна на PHP версия за секунди (от 7.4 до 8.4), автоматично route-ване на &lt;code class=&quot;language-text&quot;&gt;*.test&lt;/code&gt; domains и нулев Docker overhead. Ако правиш стандартно Laravel приложение и не ти трябват екзотични услуги, Herd те вкарва в писане на код за под минута.&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; е официалната Docker-базирана development environment на Laravel. Той обвива Docker Compose със &lt;code class=&quot;language-text&quot;&gt;sail&lt;/code&gt; CLI, което абстрахира обичайните команди (&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;Към Laravel 12 Sail идва с PHP 8.5 по подразбиране, използва &lt;code class=&quot;language-text&quot;&gt;compose.yaml&lt;/code&gt; (модерното име на файла, не &lt;code class=&quot;language-text&quot;&gt;docker-compose.yml&lt;/code&gt;) и включва Swoole за Octane out of the box. Поддържа и devcontainer generation чрез &lt;code class=&quot;language-text&quot;&gt;--devcontainer&lt;/code&gt; за интеграция с VS Code/GitHub Codespaces.&lt;/p&gt;
&lt;p&gt;Услуги по подразбиране: PHP, MySQL, Redis, Meilisearch, Mailpit и 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; е швейцарското ножче. Това е open-source Docker environment, която поддържа всеки PHP проект -- не само Laravel. Предлага 70+ предварително конфигурирани услуги и може да бъде настроена за production употреба.&lt;/p&gt;
&lt;p&gt;Все още се поддържа активно към декември 2025 (скорошни PHP-FPM и workspace image обновления). Цената е сложност: setup-ът отнема повече време, конфигурацията минава през редактиране на няколко файла, и ти трябва истинско Docker знание.&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; е модерен PHP application server, построен върху Caddy. Комбиниран с Laravel Octane, постига 4-6ms framework boot time на request -- един разработчик докладва спад на latency от 7 секунди до 66ms след преминаване към Worker Mode &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 използва FrankenPHP в своя Octane runtime в production &lt;small&gt;&lt;a href=&quot;#ref2&quot;&gt;[2]&lt;/a&gt;&lt;/small&gt;. Последният release (v1.11.2, февруари 2026) донесе 30% по-бърз CGO и 40% по-бърз garbage collection от Go 1.26.&lt;/p&gt;
&lt;p&gt;Това не е dev environment в традиционния смисъл -- това е production-grade PHP runtime, който можеш да използваш и в development. Sail включва интеграция за пускане на Octane с FrankenPHP или Swoole.&lt;/p&gt;
&lt;h2&gt;Кога какво да използваш&lt;/h2&gt;
&lt;p&gt;Ето моят честен прочит, базиран на реална употреба на тези инструменти:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Използвай Herd, ако&lt;/strong&gt; си сам или в малък екип, правиш стандартни Laravel приложения и искаш да не губиш никакво време по infrastructure. Това е най-бързият път от &quot;имам идея&quot; до &quot;пиша код&quot;. Ограничението е, че е само за macOS/Windows и безплатната версия не включва бази данни.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Използвай Sail, ако&lt;/strong&gt; екипът ти има нужда от parity между средите, зависиш от конкретни версии на услуги (Redis 7, MySQL 8, PostgreSQL 15), или работиш в CI/CD pipeline, който изисква Docker. Командата &lt;code class=&quot;language-text&quot;&gt;sail:publish&lt;/code&gt; на Sail ти позволява да customize-ваш Docker setup-а, когато надраснеш defaults.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Използвай Laradock, ако&lt;/strong&gt; работиш с няколко PHP framework-а (Symfony, Shopware, vanilla PHP), имаш нужда от екзотични услуги (Aerospike, RethinkDB, Manticore), или искаш една Docker environment за няколко проекта. Learning curve-ът е по-стръмен, но flexibility-то е без конкуренция.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Използвай custom Docker Compose setup, ако&lt;/strong&gt; си надраснал и Sail, и Laradock, и искаш пълен контрол. Това правя аз. Поддържам собствен &lt;code class=&quot;language-text&quot;&gt;compose.yaml&lt;/code&gt; с точно услугите, които ми трябват, без abstraction layer, и Docker Compose aliases, за да държа командите кратки. Иска повече работа в началото, но няма магия -- всичко е явно.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Използвай FrankenPHP + Octane, ако&lt;/strong&gt; правиш high-performance API или приложението ти е latency-sensitive. Разликата в performance-а не е маргинална -- тя е цял порядък. Струва си да се разгледа, дори ако използваш друг инструмент за general development.&lt;/p&gt;
&lt;h2&gt;Детайлите, които имат значение&lt;/h2&gt;
&lt;h3&gt;Време за setup&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Инструмент&lt;/th&gt;
&lt;th&gt;Време до първи 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;Под 1 минута&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Sail&lt;/td&gt;
&lt;td&gt;5-10 минути (image pulls)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Custom Compose&lt;/td&gt;
&lt;td&gt;30-60 минути (initial setup)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Laradock&lt;/td&gt;
&lt;td&gt;1-2 часа (пълна конфигурация)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3&gt;Customization&lt;/h3&gt;
&lt;p&gt;Sail е умишлено ограничен. Получаваш услугите, от които Laravel има нужда, и не много повече. &lt;em&gt;Можеш&lt;/em&gt; да customize-ваш, като пуснеш &lt;code class=&quot;language-text&quot;&gt;sail:publish&lt;/code&gt; и редактираш Dockerfile-овете, но в този момент поддържаш custom Docker setup със Sail abstraction-и отгоре -- най-лошото от двата свята.&lt;/p&gt;
&lt;p&gt;Laradock ти дава всичко, но изисква да разбираш какво включваш. Да активираш услуга означава да редактираш &lt;code class=&quot;language-text&quot;&gt;.env&lt;/code&gt; и евентуално &lt;code class=&quot;language-text&quot;&gt;docker-compose.yml&lt;/code&gt;, а някои услуги имат собствени configuration директории.&lt;/p&gt;
&lt;p&gt;Custom Compose ти дава точно това, което напишеш. Нито повече, нито по-малко.&lt;/p&gt;
&lt;h3&gt;Production readiness&lt;/h3&gt;
&lt;p&gt;Sail изрично не е за production. Laradock може да се конфигурира за production, но трябва да знаеш какво правиш със security hardening, resource limits и правилен networking. FrankenPHP е production-ready по design -- за това е построен.&lt;/p&gt;
&lt;h3&gt;Multi-project support&lt;/h3&gt;
&lt;p&gt;Sail: един проект на environment. Можеш да пуснеш няколко Sail instances, но ще се бият за port-ове.&lt;/p&gt;
&lt;p&gt;Laradock: проектиран за multi-project setups. Една environment, няколко проекта, shared services.&lt;/p&gt;
&lt;p&gt;Custom Compose: каквото си архитектурираш. Аз държа отделни compose файлове за всеки проект със shared network definitions.&lt;/p&gt;
&lt;h2&gt;Какво използвам аз&lt;/h2&gt;
&lt;p&gt;Custom Docker Compose. Имам aliases за всичко -- &lt;code class=&quot;language-text&quot;&gt;dcu&lt;/code&gt; за &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; за exec, &lt;code class=&quot;language-text&quot;&gt;dcefpm&lt;/code&gt; за PHP-FPM shell access, и &lt;code class=&quot;language-text&quot;&gt;sail&lt;/code&gt; function, която auto-discover-ва project root-а. Setup-ът е в моите &lt;a href=&quot;/bg/docker-compose-major-changes-since-october-2023/&quot;&gt;Docker Compose evolution notes&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Започнах с Laradock преди години, преминах към Sail, когато излезе, и накрая се установих на custom setup, защото исках да разбирам точно какво върви и защо. Всяка abstraction крие решения. Понякога това е окей. Понякога тези скрити решения създават проблеми, които са трудни за debugging, защото не можеш да ги видиш.&lt;/p&gt;
&lt;p&gt;И все пак, ако започвах нов Laravel проект днес с екип, който не се интересува от Docker internals, бих използвал Sail. А ако mentor-вах някого, който тепърва влиза в Laravel, бих му казал да инсталира Herd и веднага да започне да пише код.&lt;/p&gt;
&lt;h2&gt;Други опции, които си струва да се споменат&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-базиран, с добра Laravel поддръжка, активна 2026 roadmap с планирана Gitpod интеграция. Струва си да го оцениш, ако го използваш за други CMS проекти (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; -- още един Docker abstraction layer с Laravel plugin (v1.10.0, януари 2026). Подобна философия на Sail, но framework-agnostic.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Valet&lt;/strong&gt; -- предшественикът на Herd. Все още работи, но Herd го е заместил за повечето use cases.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3&gt;References&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;Setup and Boost Laravel with FrankenPHP Worker Mode&lt;/a&gt; -- &lt;em&gt;Real-world performance comparison: 7s to 66ms latency.&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;How Laravel Cloud Uses FrankenPHP in Production&lt;/a&gt; -- &lt;em&gt;DevConf talk on Laravel Cloud&apos;s 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 Documentation&lt;/a&gt; -- &lt;em&gt;Official Sail docs with PHP 8.5 and compose.yaml changes.&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;Official site for the native Laravel development environment.&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;February 2026 release with performance and security updates.&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 on GitHub&lt;/a&gt; -- &lt;em&gt;Still maintained, Dec 2025 updates.&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;The Current State of Local Laravel Development&lt;/a&gt; -- &lt;em&gt;Andrew Schmelyun&apos;s ecosystem overview.&lt;/em&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h3&gt;Свързани публикации&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;/bg/docker-compose-major-changes-since-october-2023/&quot;&gt;Docker Compose Evolution: какво се промени и защо има значение&lt;/a&gt; -- промените в Docker Compose, които засягат всички тези инструменти.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/bg/php-8-5-new-features-pipe-operator-guide/&quot;&gt;PHP 8.5: обиколка на задаващите се features&lt;/a&gt; -- какво идва в PHP версията, към която Laravel 12 default-ва.&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 на macOS с phpenv&lt;/a&gt; -- когато ти трябва конкретен PHP setup извън Docker.&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[Най-лошият лицемер: приказката на едно гумено пате за любовта към себе си]]></title><description><![CDATA[От Борис до един скъп приятел:
~ Човекът, когото винаги ми беше трудно да разбера, но който се оказа най-разбиращият от всички. ~ Здравей…]]></description><link>https://bdteo.com/bg/worst-hypocrite-rubber-duck-tale/</link><guid isPermaLink="false">https://bdteo.com/bg/worst-hypocrite-rubber-duck-tale/</guid><pubDate>Wed, 17 Jul 2024 12:00:00 GMT</pubDate><content:encoded>&lt;p&gt;От Борис до един скъп приятел:
~ Човекът, когото винаги ми беше трудно да разбера, но който се оказа най-разбиращият от всички. ~&lt;/p&gt;
&lt;p&gt;Здравей. Пак здравей. Може ли пак да се срещнем за първи път?
Да. Мисля, че този път ще е по-яко. Ще бъда по-сиренясал от всякога 🧀, ще се опитвам да те разсмивам повече, но пак понякога ще се провалям, или през повечето време, или каквото там...
Почти ще е като миналия път, но няма да е като миналия път. Бележка: Можеш да се престориш, че последното ми изречение има смисъл, само заради изтънчеността 🥸&lt;/p&gt;
&lt;p&gt;Нека започна с един малък речник, или въведение, или малка база знания:&lt;/p&gt;
&lt;h2&gt;Едно „начало“ наобратно е „олачан“&lt;/h2&gt;
&lt;p&gt;Просто ми трябваше ново заглавие, за да стане абзацът ми по-красив. Както и да е. Нов аз. Нова ти. Стари дрехи и някои стари лоши навици. Пуф!&lt;/p&gt;
&lt;h2&gt;Огледало на стената&lt;/h2&gt;
&lt;p&gt;Много ми липсваше и дълго си припомнях грешките си. Огледалата наистина съществуват, за да ни правят по-красиви и по-малко мръсни.&lt;/p&gt;
&lt;h2&gt;Гумено пате&lt;/h2&gt;
&lt;p&gt;Това съм аз. Аз съм гумено пате. Направен съм от гума, а това означава, че и главата ми е направена от гума.&lt;/p&gt;
&lt;h2&gt;Гумените патета не „крякат“. Или пък да?&lt;/h2&gt;
&lt;p&gt;В моя скромен житейски опит като гумено пате аз „крякам“ и го правя доста често. 😆&lt;/p&gt;
&lt;h3&gt;Крякащо гумено пате кряка&lt;/h3&gt;
&lt;p&gt;Квак! Квак! Квак! - каза патето и пак изкряка. Невероятен звук! Нали?&lt;/p&gt;
&lt;h3&gt;Гумено пате с характер&lt;/h3&gt;
&lt;p&gt;Аз съм най-милото гумено пате от всички, каза гуменото пате и изкряка развълнувано! Квак!&lt;/p&gt;
&lt;h3&gt;Истинската гибел на гумените патета&lt;/h3&gt;
&lt;p&gt;Крякането може да бъде доста изтощително, знаеш ли. Можеш да изкрякаш красиво само ограничен брой пъти с един свеж дъх въздух.
Но все пак можеш да се опиташ да крякаш красиво, когато си празен? Нали?
Разбира се, че можеш! Можеш да се опиташ да изкрякаш! Можеш дори да дадеш най-доброто си бързо квакване от гумата си, за да помогнеш на другите празни гумени патета да си поемат дъх въздух.&lt;/p&gt;
&lt;h2&gt;Кратък разказ за две наполовина празни гумени патета&lt;/h2&gt;
&lt;p&gt;„Знаеш ли какво?“ - каза Duckitty
„Какво?“ - каза Duckelton
„Когато крякам твърде много, вече не мога да крякам, а искам да крякам, и това ме натъжава.“ - отговори Duckitty
Duckleton придоби много сериозен вид, смръщи вежди и каза с мъдър глас:
„Да. И аз се чувствам така!“&lt;/p&gt;
&lt;h2&gt;Гуменото пате, което мразеше да мълчи&lt;/h2&gt;
&lt;p&gt;Duckleton мразеше да мълчи и издаваше силно „квак“ винаги когато можеше. Понякога беше доста празен и се чудеше дали е достатъчно жълт, за да кряка.
Но, разбираш ли, фабриката, която беше направила Ducleton и Duckitty, ги беше направила и двамата съвършено жълти.
Duckleton беше гумено пате с характер. Той мислеше, че може и трябва да надкряка всяко друго пате, за да го спаси от срама да не кряка, когато е празно.
Duckitty понякога имаше подобни мисли, или поне Duckleton мислеше така, когато беше с ума си. (Понякога не беше 😆)
Но Duckleton беше гордо гумено пате. Гумено пате, направено в най-добрата фабрика в света, която от векове правеше най-добрите крякащи жълти гумени патета. Поне Duckleton мислеше така, докато не срещна Duckitty.
Щом я срещна, той беше толкова смаян от гласа ѝ, че реши, че тя сигурно е направена в някаква извънземна фабрика, много по-добра от земната фабрика, от която идваше той.
Беше щастлив, че има възможността просто да се възхищава на това прекрасно и съвършено гумено пате, което беше много по-добро от него (така мислеше той) и се казваше Duckitty.
„Е, толкова е хубаво да я гледам. Тя е толкова жълта. Тя е толкова мила. Има много по-красив глас от мен. Аз съм щастливо гумено пате.“ - мислеше си Duckleton.&lt;/p&gt;
&lt;h2&gt;Кратък, но малко тъжен разказ&lt;/h2&gt;
&lt;p&gt;Duckleton и Duckitty бяха щастливи и крякаха така, сякаш това беше най-доброто състезание по крякане в света и в открития космос.
Веднъж едно човешко дете дойде и стисна Duckleton. Той не можеше да кряка известно време, но все още копнееше да го прави, затова остана мълчалив, без да се оплаква, пое си дълбоко дъх и двамата продължиха да крякат щастливо.
Мина известно време и Duckitty също беше взета от едно гадно човешко дете. Човешкото дете изстиска целия въздух от Duckitty, но Duckleton беше твърде зает да кряка, за да забележи.
Duckitty беше добре, но трябваше да си поеме малко въздух.
Duckleton беше тъжен отвъд отчаянието. Той мислеше, че любимото му пате се е счупило и че пак ще е завинаги сам до края на Вселената. Просто беше забравил, че същото дете го беше стискало и него известно време по-рано.
Duckleton беше горд, но глупав, все пак имаше малка гумена глава. Когато Duckitty изкряка, Duckleton побесня. Той хем беше щастлив, че тя е жива, хем беше малко обезсърчен, защото си мислеше, че Duckitty може да преживее стискане от човешко дете по-добре от него.
В крайна сметка тя беше толкова по-съвършена от него, нали?&lt;/p&gt;
&lt;h2&gt;Вината на Duckleton&lt;/h2&gt;
&lt;p&gt;Duckleton беше най-лошият лицемер от всички и най-накрая го разбра. Беше тъжен, но си помисли: Нека се извиня на Duckitty и ѝ покажа малко благодарност, че е толкова съвършена, каквато е.&lt;/p&gt;
&lt;h2&gt;Епилог&lt;/h2&gt;
&lt;p&gt;Скъпи мой приятелю, ти беше съвършена през последните 8 години. Беше съвършена всеки един ден. Много съжалявам, че бях такъв лицемер, но се случват лоши неща, знаеш. И ние пак продължаваме.
Моля те, обичай себе си много повече. Аз съм глупав. Ти си умна. Или каквото там... Но обичай себе си много повече, защото си съвършена такава, каквато си!
Всъщност ти си най-съвършеното жълто гумено пате в света. Поне аз мисля така. Гумената ми глава е малка. Но, ура! Все още мога да мисля. 🫶
Квак!&lt;/p&gt;
&lt;p&gt;Квак, Квак, Квак
~ Duckleton&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Дискретни представяния в RL: изследователските прозрения на Edan Meyer]]></title><description><![CDATA[Замислял ли си се как AI агентите се научават да разбират сложни среди и да действат в тях? Edan Meyer, изследовател в областта на…]]></description><link>https://bdteo.com/bg/discrete-representations-reinforcement-learning-insights/</link><guid isPermaLink="false">https://bdteo.com/bg/discrete-representations-reinforcement-learning-insights/</guid><pubDate>Mon, 15 Jul 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Замислял ли си се как AI агентите се научават да разбират сложни среди и да действат в тях? Edan Meyer, изследовател в областта на reinforcement learning (RL), проучва интригуващ подход, който може да промени начина, по който мислим за обучението на AI. Нека се гмурнем в любопитната му работа върху дискретните представяния в RL.&lt;/p&gt;
&lt;h2&gt;Силата на представянето&lt;/h2&gt;
&lt;p&gt;Представи си, че се опитваш да научиш компютър да играе видеоигра. Как би представил състоянието на играта така, че компютърът да може да го разбере и да се учи от него? Тук идва representation learning, а то е ключова част от създаването на ефективни AI агенти.&lt;/p&gt;
&lt;p&gt;Edan Meyer, чиято работа можеш да видиш в неговия &lt;a href=&quot;https://www.youtube.com/@EdanMeyer&quot;&gt;YouTube канал&lt;/a&gt;, изследва конкретен тип представяния, наречени дискретни представяния. Неговото изследване, подробно описано в &lt;a href=&quot;https://arxiv.org/abs/2312.01203&quot;&gt;статия, достъпна в arXiv&lt;/a&gt;, хвърля светлина върху това защо тези представяния може да са особено полезни в определени RL сценарии.&lt;/p&gt;
&lt;h2&gt;Две години изследвания в 13 минути&lt;/h2&gt;
&lt;p&gt;Edan е събрал две години от магистърското си изследване в увлекателно 13-минутно видео със заглавие &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;. В него той разбива сложни концепции до смилаеми обяснения и прави работата си достъпна за по-широка публика.&lt;/p&gt;
&lt;p&gt;Както Edan описва във video description-а:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&quot;Това е моето изследване върху representation learning и model learning в контекста на reinforcement learning. Две години работа, и най-накрая мога да говоря за магистърското си изследване! Статията беше приета на Reinforcement Learning Conference (RLC) 2024.&quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Това видео е чудесна отправна точка за всеки, който иска да разбере основите на изследването му, без веднага да се гмурка в цялата академична статия.&lt;/p&gt;
&lt;h2&gt;Какво са дискретните представяния?&lt;/h2&gt;
&lt;p&gt;Традиционно много RL системи използват непрекъснати представяния - мисли за тях като за вектори от десетични числа, които могат да приемат всякаква стойност. Дискретните представяния, от друга страна, приличат повече на поредица от въпроси с избираем отговор. Всеки &quot;слот&quot; в представянето може да приеме само една стойност от фиксиран брой възможности.&lt;/p&gt;
&lt;p&gt;Както Edan обяснява във видеото си, на пръв поглед това може да изглежда ограничаващо. Все пак една непрекъсната стойност може да представя безкрайно много състояния, докато дискретната стойност е много по-ограничена. Тогава защо изобщо да използваме дискретни представяния?&lt;/p&gt;
&lt;h2&gt;Изненадващите ползи&lt;/h2&gt;
&lt;p&gt;Изследването на Edan открива няколко любопитни предимства на дискретните представяния:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;По-добри world models с по-малък капацитет&lt;/strong&gt;: Когато AI се опитва да научи модел на средата си (&quot;world model&quot;), дискретните представяния му позволяват да улови по-точна информация с по-малко изчислителна мощ. Това е особено вярно, когато моделът няма достатъчно капацитет да представи идеално всичко в средата - често срещан сценарий при сложни проблеми от реалния свят.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;По-бърза адаптация&lt;/strong&gt;: В експерименти, при които средата се променя с времето, агентите, използващи дискретни представяния, успяват да се адаптират по-бързо към тези промени. Това може да е решаващо за AI системи, които трябва да работят в динамични и непредвидими среди.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Ефективно обучение&lt;/strong&gt;: Макар дискретните представяния първоначално да може да отнемат повече време за научаване, след като са установени, те позволяват по-бързо обучение и адаптация както при задачи за world modeling, така и при policy learning.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;Защо това има значение?&lt;/h2&gt;
&lt;p&gt;Последствията от работата на Edan стигат далеч отвъд простите grid-world експерименти. Както той посочва във видеото си, реалният свят е неизмеримо по-сложен от всяка симулация, която можем да създадем. В такива среди е невъзможно един AI да научи всичко - ключът е адаптацията.&lt;/p&gt;
&lt;p&gt;Дискретните представяния изглежда предлагат мощен инструмент за създаване на AI системи, които могат бързо да се адаптират към нови ситуации, дори когато няма как да моделират всеки аспект на средата си. Това може да промени играта за приложения от роботика до сложни стратегически игри и отвъд тях.&lt;/p&gt;
&lt;h2&gt;По-надълбоко&lt;/h2&gt;
&lt;p&gt;За тези, които се интересуват от техническите детайли, статията на Edan разглежда увлекателни аспекти на това защо дискретните представяния работят толкова добре. Например той открива, че не всички дискретни представяния са еднакви - фактори като sparsity и binarity играят важна роля за тяхната ефективност.&lt;/p&gt;
&lt;h2&gt;Заключение&lt;/h2&gt;
&lt;p&gt;Работата на Edan Meyer върху дискретните представяния в reinforcement learning предлага вълнуващи прозрения за това как можем да създаваме по-адаптивни и по-ефективни AI системи. Като оспорва конвенционалната мъдрост за това как трябва да представяме информация за AI, неговото изследване отваря нови възможности за създаване на агенти, които могат да се справят в сложни и динамични среди.&lt;/p&gt;
&lt;p&gt;Независимо дали си AI изследовател, студент по machine learning, или просто човек, увлечен от фронтира на технологиите, работата на Edan дава убедителен поглед към бъдещето на изкуствения интелект. Непременно виж неговия &lt;a href=&quot;https://www.youtube.com/@EdanMeyer&quot;&gt;YouTube канал&lt;/a&gt;, обяснителното му &lt;a href=&quot;https://www.youtube.com/watch?v=s8RqGlU5HEs&quot;&gt;видео&lt;/a&gt; и &lt;a href=&quot;https://arxiv.org/abs/2312.01203&quot;&gt;статията&lt;/a&gt; за по-задълбочено разглеждане на тези идеи.&lt;/p&gt;
&lt;p&gt;Помни: в бързо движещия се свят на AI изследванията днешните експериментални техники може да се окажат утрешните пробивни технологии. Дискретните представяния може би са ключът към отключването на по-способни и по-адаптивни AI системи в близко бъдеще.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Историята на AI в Google: обещания, акции и пазарно влияние]]></title><description><![CDATA[Пътят на Google през разработването и маркетинга на AI технологии като серията Gemini
е любопитен казус за взаимодействието между…]]></description><link>https://bdteo.com/bg/google-ai-ambitions-historical-analysis-promises-stock-market-impact/</link><guid isPermaLink="false">https://bdteo.com/bg/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;Пътят на Google през разработването и маркетинга на AI технологии като серията Gemini
е любопитен казус за взаимодействието между корпоративни обещания, представяне на
борсата и технологични иновации. Този анализ разглежда конкретни исторически моменти,
в които AI обещанията на Google са повлияли значимо върху цените на акциите, заедно с
преглед на успехите и провалите на компанията.&lt;/p&gt;
&lt;p&gt;Навлизането на технологичния гигант в AI е белязано от амбициозни проекти и смели
твърдения. От представянето на TensorFlow през 2015 г., което утвърди Google като
лидер в изследванията и разработката на AI, до пускането на Google Assistant през
2016 г., което засили конкурентоспособността му срещу съперници като Alexa на Amazon
и Siri на Apple, Google постоянно се стреми да разширява границите на възможностите
на AI &lt;small&gt;&lt;a href=&quot;#ref1&quot;&gt;[1]&lt;/a&gt;&lt;/small&gt;.&lt;/p&gt;
&lt;h3&gt;Ключови исторически етапи в развитието на AI в Google&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Представяне на TensorFlow (2015)&lt;/strong&gt;: Google направи TensorFlow, своя framework за
машинно обучение, open source, и той бързо стана широко популярен. Този ход помогна
Google да се утвърди като лидер в AI изследванията и разработката, с положителен
ефект върху пазарното възприятие &lt;small&gt;&lt;a href=&quot;#ref1&quot;&gt;[1]&lt;/a&gt;&lt;/small&gt;. TensorFlow се използва
в различни приложения - от подобряване на резултатите при търсене до задвижване на
автономни автомобили.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Пускане на Google Assistant (2016)&lt;/strong&gt;: Представянето на Google Assistant засили
конкурентоспособността на Google в AI спрямо съперници като Alexa на Amazon и Siri
на Apple. Пазарът прие това добре, като сигнал за потенциален растеж в потребителски
интерфейси, задвижвани от AI &lt;small&gt;&lt;a href=&quot;#ref2&quot;&gt;[2]&lt;/a&gt;&lt;/small&gt;. Google Assistant беше
интегриран в смартфони, устройства за умен дом и автомобили, превръщайки се в
ключов играч на пазара на гласови асистенти.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Напредък в квантовите изчисления (2019)&lt;/strong&gt;: Google обяви пробив в квантовото
превъзходство, твърдейки, че квантовият му компютър може да извършва изчисления
отвъд възможностите на традиционните суперкомпютри. Това съобщение доведе до кратък
скок в цената на акциите и показа ентусиазма на инвеститорите към върховите
технологични възможности на Google &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;Представяне на борсата и AI етапи&lt;/h3&gt;
&lt;p&gt;Борсата е реагирала различно на AI развитието на Google. Значими съобщения, като
пробиви в квантовите изчисления и нови AI продуктови премиери, обикновено водят до
краткосрочно покачване на цената на акциите. Дългосрочното влияние върху цените обаче
е по-тясно свързано с реалното внедряване и търговския успех на тези технологии.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Етапи на S&amp;#x26;P 500&lt;/strong&gt;: Големите AI съобщения на Google често съвпадат с по-широки
пазарни тенденции. Например по-широкият bull market през 2021 г. видя Google да
достига нови върхове в цената на акциите си паралелно със значим напредък в AI,
отразявайки силно инвеститорско доверие в растеж, движен от технологии &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&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;
    Fig1. - Етапи в акциите на Google.
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h3&gt;Успехи и провали&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Успехи&lt;/strong&gt;: AI на Google постигна значителен успех в области като езиков превод,
разпознаване на изображения и технологии за автономно шофиране чрез Waymo. Тези
успехи помогнаха да се затвърди репутацията на Google като технологичен иноватор
&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;Провали&lt;/strong&gt;: Не всички AI инициативи на Google отговориха на очакванията на пазара.
Например силно очакваният проект Google Glass не успя да резонира с потребителите,
което доведе до прекратяването му. Продуктът се сблъска с опасения за личната
неприкосновеност и не успя да предложи убедителен use case за средния потребител.
По подобен начин забавянията и прекомерните обещания около Gemini 1.5 Pro доведоха
до недоволство и скептицизъм сред потребителите &lt;small&gt;&lt;a href=&quot;#ref6&quot;&gt;[6]&lt;/a&gt;&lt;/small&gt;. Google
се учи от тези провали, като се фокусира върху по-прагматични AI приложения и
подобрява комуникацията си с потребителите.&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;AI етапи на технологични компании&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;Fig2. - AI етапи на технологични компании.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h3&gt;Пазарно влияние и бъдеща перспектива&lt;/h3&gt;
&lt;p&gt;Пазарното влияние на AI усилията на Google е значимо - то влияе не само на цените на
акциите на компанията, но и на посоката на по-широката технологична индустрия. AI
секторът продължава да бъде основен фокус за инвеститорите, както се вижда и от
представянето на S&amp;#x26;P 500, където технологичните акции играят съществена роля
&lt;small&gt;&lt;a href=&quot;#ref4&quot;&gt;[4]&lt;/a&gt;&lt;/small&gt;.&lt;/p&gt;
&lt;p&gt;Напред способността на Google да изпълни AI обещанията си и да преодолее
предизвикателствата на технологичното внедряване ще бъде решаваща. Стратегията на
компанията да увеличи прозрачността и да управлява по-ефективно потребителските
очаквания може да определи бъдещата ѝ пазарна позиция и доверието на инвеститорите.&lt;/p&gt;
&lt;p&gt;Една ключова област за наблюдение е напредъкът на Google със серията AI модели Gemini.
Gemini 1.5 Pro, обявен през февруари 2024 г., обещава значителен скок във възможностите
на AI, но се сблъска с трудности при rollout-а си &lt;small&gt;&lt;a href=&quot;#ref6&quot;&gt;[6]&lt;/a&gt;&lt;/small&gt;. Начинът,
по който Google ще премине през тези трудности и ще изпълни обещанията си, вероятно
ще има осезаем ефект върху AI репутацията и пазарната позиция на компанията.&lt;/p&gt;
&lt;p&gt;Амбициозният напредък на Google в изкуствения интелект, особено чрез серията Gemini,
означава преобразяваща ера в AI технологиите. Представянето на Gemini 1.5 Pro като
част от тази серия подчертава ангажимента на Google да разширява границите на AI
възможностите. Rollout-ът на Gemini 1.5 Pro обаче не мина без предизвикателства, а те
са важни за разбирането както на потенциала, така и на ограниченията на толкова
напреднали AI модели.&lt;/p&gt;
&lt;h3&gt;Експертни мнения за Gemini 1.5 Pro&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Представяне и възможности&lt;/strong&gt;: Gemini 1.5 Pro, изграден върху Mixture-of-Experts (MoE)
архитектура, предлага значителни подобрения спрямо предишни модели, включително
огромно увеличение на context window до 10 милиона токена. Това подобрение позволява
по-добра работа със сложни задачи, включващи големи обеми данни в различни формати -
текст, код, визия и аудио &lt;small&gt;&lt;a href=&quot;#ref7&quot;&gt;[7]&lt;/a&gt;&lt;/small&gt;. Според Stephen Oladele от
Encord &quot;Gemini 1.5 Pro поддържа почти перфектно припомняне в целия контекст и използва
mixture-of-experts архитектура за по-ефективно обучение и по-качествени отговори&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;Предизвикателства при rollout-а&lt;/strong&gt;: Въпреки напредналите си възможности rollout-ът
на Gemini 1.5 Pro се сблъска с няколко предизвикателства. Моделът в момента е в private
preview, а общата му достъпност е планирана за по-късен етап, което показва поетапен
подход към внедряването &lt;small&gt;&lt;a href=&quot;#ref7&quot;&gt;[7]&lt;/a&gt;&lt;/small&gt;. Тази предпазлива стратегия
може да се дължи на нуждата моделът да бъде донастроен допълнително и да се гарантира,
че отговаря на високите очаквания, поставени от предшествениците му и от пазара.&lt;/p&gt;
&lt;h3&gt;Погледът на индустриалните анализатори&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Влияние върху AI индустрията&lt;/strong&gt;: Индустриалните анализатори виждат серията Gemini
като значителна стъпка напред за Google, която потенциално задава нови стандарти за
това какво могат да постигат AI моделите. Очаква се серията Gemini, особено 1.5 Pro,
да засили конкурентоспособността на Google срещу други технологични гиганти като
OpenAI и Microsoft, които също агресивно развиват своите AI възможности
&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;Пазарни последици&lt;/strong&gt;: Напредъкът в Gemini 1.5 Pro вероятно ще повлияе на различни
сектори, включително здравеопазване, финанси и други, като позволи по-сложни AI
приложения. Това може да доведе до промени в пазарната динамика, при които компаниите,
които ефективно интегрират такива напреднали AI технологии, ще получат значително
конкурентно предимство &lt;small&gt;&lt;a href=&quot;#ref7&quot;&gt;[7]&lt;/a&gt;&lt;/small&gt;.&lt;/p&gt;
&lt;h3&gt;Изводи от ръководителите на Google&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Бъдещи планове и етични съображения&lt;/strong&gt;: Ръководители на Google, включително Sundar
Pichai, подчертават ангажимента на компанията към отговорно развитие на AI. Pichai
изтъква важността AI напредъкът да бъде съгласуван с етични насоки и да се гарантира,
че тези технологии се използват в полза на обществото &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;. Този подход е решаващ, докато AI става все
по-способен и все по-интегриран в ежедневни приложения.&lt;/p&gt;
&lt;h3&gt;Навигиране на предизвикателства и възможности&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Справяне с техническите предизвикателства&lt;/strong&gt;: За да премине през предизвикателствата
напред, Google трябва да продължи да инвестира в изследвания и разработка, за да
адресира проблеми като надеждност на моделите и етични опасения. Подходът на компанията
за постепенно пускане на Gemini 1.5 Pro подсказва стратегия, фокусирана върху
намаляване на рисковете, преди те да се превърнат в значителни проблеми
&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;Разширяване на пазарните възможности&lt;/strong&gt;: Google може да използва възможностите на
Gemini 1.5 Pro, за да създаде нови пазарни възможности, особено в сектори, които
изискват работа с големи datasets и сложни сценарии за решаване на проблеми. Като
предоставя инструменти, които опростяват интеграцията на AI в бизнес процеси, Google
може да помогне на индустриите да трансформират операциите си и да постигнат по-голяма
ефективност &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;Етично развитие на AI&lt;/strong&gt;: С усилването на AI технологиите етичните последици стават
все по-значими. Продължаващият ангажимент на Google към отговорно развитие на AI,
демонстриран чрез AI принципите и governance frameworks на компанията, ще бъде ключов
за поддържане на общественото доверие и регулаторното съответствие
&lt;small&gt;&lt;a href=&quot;#ref11&quot;&gt;[11]&lt;/a&gt;&lt;/small&gt;.&lt;/p&gt;
&lt;p&gt;В заключение, макар rollout-ът на Gemini 1.5 Pro да носи предизвикателства, той предлага
и съществени възможности за Google да води в AI пространството. Като продължи да се
фокусира върху подобрения в представянето, етично развитие на AI и пазарно разширяване,
Google може ефективно да премине през тези предизвикателства и да зададе нови стандарти
за това какво AI може да постига в различни индустрии.&lt;/p&gt;
&lt;h3&gt;Заключение&lt;/h3&gt;
&lt;p&gt;AI пътят на Google показва сложната динамика между технологични иновации, пазарни
очаквания и корпоративна стратегия. Макар компанията да има както забележими успехи,
така и неуспехи, продължаващите ѝ усилия в AI продължават да привличат значително
пазарно внимание. Инвеститорите и пазарните наблюдатели вероятно ще следят внимателно
способността на Google да превърне AI възможностите си в устойчив растеж и пазарно
лидерство.&lt;/p&gt;
&lt;p&gt;Докато AI пейзажът продължава да се развива бързо, позицията на Google като AI пионер
ще бъде подложена на изпитание. Способността на компанията да балансира амбициозна
иновация с реалистично изпълнение, прозрачна комуникация и ефективно управление на
очакванията ще бъде критичен фактор за дългосрочния ѝ успех в AI сферата и за общото
ѝ пазарно представяне.&lt;/p&gt;
&lt;h3&gt;Източници&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 - AI стратегията на Google&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 котировка&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&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 инструменти&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 1.5 Pro AI&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 - AI бъдещето на Google&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 фотореализъм: настройки и GPU лимити]]></title><description><![CDATA[Обновено март 2026. Тази статия първоначално беше написана през май 2023 г., когато SD 1.5 при 512x512 беше стандартът, а RTX 3090 беше…]]></description><link>https://bdteo.com/bg/pushing-the-stable-diffussion-limits/</link><guid isPermaLink="false">https://bdteo.com/bg/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;Обновено март 2026.&lt;/strong&gt; Тази статия първоначално беше написана през май 2023 г., когато SD 1.5 при 512x512 беше стандартът, а RTX 3090 беше върховият хардуер. Всичко се промени. Flux 2, SDXL fine-tunes, SD 3.5, ControlNet и RTX 5090 напълно предефинираха възможното. Това е текущото състояние.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Разликата между AI-генерирани изображения и истински фотографии почти се затвори. През 2023 г. &quot;фотореалистично&quot; означаваше &quot;почти убедително, ако присвиеш очи&quot;. През 2026 г. най-добрите модели произвеждат изображения, които наистина е трудно да различиш от професионална фотография.&lt;/p&gt;
&lt;p&gt;Ето как да стигнеш дотам.&lt;/p&gt;
&lt;h2&gt;Текущият пейзаж на фотореализма&lt;/h2&gt;
&lt;p&gt;Моделът, който избереш, има по-голямо значение от всяка настройка, която пипаш. Ето къде стоят нещата:&lt;/p&gt;
&lt;h3&gt;Flux 2 -- Новият крал&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://bfl.ai/models/flux-2&quot;&gt;Flux 2&lt;/a&gt; от Black Forest Labs (пуснат през ноември 2025 г.) вероятно е най-добрият open-weight модел за фотореализъм през 2026 г. &lt;small&gt;&lt;a href=&quot;#ref1&quot;&gt;[1]&lt;/a&gt;&lt;/small&gt;. Той произвежда изображения с естествено осветление, точни текстури на кожата и кохерентна композиция, която съперничи на професионална фотография. Adobe интегрира Flux (Kontext Pro) във Photoshop през септември 2025 г. &lt;small&gt;&lt;a href=&quot;#ref2&quot;&gt;[2]&lt;/a&gt;&lt;/small&gt; -- това ти казва къде е доверието на индустрията.&lt;/p&gt;
&lt;p&gt;Flux също има изключително добро разбиране на естествен език. Можеш да опишеш какво искаш на обикновен английски, без супата от ключови думи, която SD 1.5 изискваше.&lt;/p&gt;
&lt;h3&gt;SDXL Fine-Tunes -- Работните коне&lt;/h3&gt;
&lt;p&gt;За SDXL-базиран фотореализъм това са текущите лидери:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Juggernaut XL v9/v10&lt;/strong&gt; -- изборът по подразбиране за кинематографичен, фотографски output. Най-популярен сред фотографи и filmmakers.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Realistic Vision&lt;/strong&gt; -- fine-tuned специално за реалистични текстури, осветление и точност на лица.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;EpicRealism&lt;/strong&gt; -- изключителен фин детайл и естествено осветление.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Тези модели имат огромна community поддръжка, обширни LoRA библиотеки и предвидимо поведение. Ако Flux ти се струва твърде нов или workflow-ът ти е изграден върху SDXL, това са отлични варианти.&lt;/p&gt;
&lt;h3&gt;SD 3.5 Large&lt;/h3&gt;
&lt;p&gt;Флагманът на Stability AI използва новата Multimodal Diffusion Transformer (MMDiT) архитектура -- фундаментално различен подход от SDXL. Технически е впечатляващ, но екосистемата е по-малка. SD 3.0 беше deprecated през април 2025 г., така че се увери, че си на 3.5 &lt;small&gt;&lt;a href=&quot;#ref3&quot;&gt;[3]&lt;/a&gt;&lt;/small&gt;.&lt;/p&gt;
&lt;h2&gt;Проверка с реалността за GPU&lt;/h2&gt;
&lt;p&gt;Хардуерните изисквания се покачиха значително.&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;Възможности за фотореализъм&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 фотореализъм. 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 при 1024x1024. 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;Сладката точка. Спокойно се справя със SDXL, Flux и SD 3.5 при 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;Всичко, включително 4K генериране и 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 карти&lt;/td&gt;
&lt;td&gt;8GB&lt;/td&gt;
&lt;td&gt;Минимумът, който става, с VRAM management-а на ComfyUI. Не е удобно&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Сладката точка от 2023 г. -- &quot;512x512 на RTX 3080&quot; -- вече е древна история. &lt;strong&gt;1024x1024 сега е стандартната резолюция&lt;/strong&gt;, а ти искаш поне 16GB VRAM, за да работиш без постоянна фрустрация. При 24GB започва да става комфортно.&lt;/p&gt;
&lt;p&gt;Конкретно за фотореализъм повече VRAM означава, че можеш да пускаш по-големи модели, по-високи резолюции и ControlNet едновременно, без offloading към CPU.&lt;/p&gt;
&lt;h2&gt;Настройки за фотореализъм&lt;/h2&gt;
&lt;h3&gt;Sampler&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;DPM++ 2M Karras&lt;/strong&gt; при 25-30 steps. Това е установеният консенсус за SDXL фотореализъм -- най-доброто съотношение скорост към качество. Ако искаш малко повече детайл при нисък брой steps, смени на &lt;strong&gt;DPM++ SDE Karras&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;За Flux: използвай default sampler-а при 20-30 steps.&lt;/p&gt;
&lt;h3&gt;CFG&lt;/h3&gt;
&lt;p&gt;За SDXL фотореализъм: &lt;strong&gt;7-9&lt;/strong&gt;. Това дава силно придържане към prompt-а без пренаситения, преготвен вид, който се появява над 10.&lt;/p&gt;
&lt;p&gt;За SD 3.5: започни по-ниско (&lt;strong&gt;3-5&lt;/strong&gt;) -- guidance механизмът работи различно.&lt;/p&gt;
&lt;p&gt;За Flux: следвай model-specific препоръките, но обикновено по-ниско от SDXL.&lt;/p&gt;
&lt;h3&gt;Резолюция&lt;/h3&gt;
&lt;p&gt;Генерирай при native резолюцията на модела (1024x1024 за SDXL/SD 3.5/Flux), после &lt;strong&gt;upscale&lt;/strong&gt; за по-висока резолюция. Не се опитвай да генерираш директно при 2048x2048 -- ще получиш артефакти, дублирани елементи и проблеми с композицията.&lt;/p&gt;
&lt;p&gt;Опции за upscaling: hi-res fix в A1111 или специализирани upscaling nodes в ComfyUI (4x-UltraSharp, ESRGAN).&lt;/p&gt;
&lt;h3&gt;Prompting за фотореализъм&lt;/h3&gt;
&lt;p&gt;Най-голямата промяна от 2023 г.: &lt;strong&gt;пиши естествено, не с ключови думи&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;SD 1.5 имаше нужда от prompts като:&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 и Flux разбират:&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;Подходът със супата от ключови думи все още работи в SDXL, но естественият език произвежда по-кохерентни резултати. Flux особено блести с описателни, разговорни prompts.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Negative prompts:&lt;/strong&gt; Дръж ги минимални. Започни без никакви, после добавяй конкретни поправки. &quot;cartoon, illustration, painting&quot; обикновено е достатъчно, за да останат нещата фотореалистични. Виж &lt;a href=&quot;/stable-difussion-cheat-sheet/&quot;&gt;cheat sheet-а&lt;/a&gt; за пълната промяна във философията на negative prompts.&lt;/p&gt;
&lt;h2&gt;ControlNet променя всичко&lt;/h2&gt;
&lt;p&gt;Ако си сериозен за фотореалистична композиция, ControlNet не подлежи на преговори. Той ти позволява да контролираш структурата на изображението чрез:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Depth maps&lt;/strong&gt; -- поддържат пространствени отношения и перспектива&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Canny edge detection&lt;/strong&gt; -- запазва контури и форми&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;OpenPose&lt;/strong&gt; -- контролира човешка поза и пропорции на тялото&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Surface normals&lt;/strong&gt; -- реалистично взаимодействие на осветлението с повърхности&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;ControlNet модели вече са налични за SDXL, Flux и SD 3.5 &lt;small&gt;&lt;a href=&quot;#ref5&quot;&gt;[5]&lt;/a&gt;&lt;/small&gt;. Multi-ControlNet (stacking на няколко controls) ти дава прецизен контрол върху композицията, който prompt engineering сам не може да постигне.&lt;/p&gt;
&lt;p&gt;Workflow-ът: вземаш референтна снимка, извличаш depth map или pose, използваш го като ControlNet input и генерираш фотореалистично изображение със същата композиция.&lt;/p&gt;
&lt;h2&gt;Скорост срещу качество&lt;/h2&gt;
&lt;p&gt;Ако ти трябват бързи итерации (concept work, prompt testing), използвай &lt;strong&gt;SDXL Lightning&lt;/strong&gt; -- той генерира качествени 1024px изображения в 2-8 steps &lt;small&gt;&lt;a href=&quot;#ref6&quot;&gt;[6]&lt;/a&gt;&lt;/small&gt;. По-добро качество от SDXL Turbo при по-високи резолюции.&lt;/p&gt;
&lt;p&gt;За финален output се върни към пълен SDXL или Flux с 25-30 steps. Разликата се вижда.&lt;/p&gt;
&lt;h2&gt;Практичният workflow&lt;/h2&gt;
&lt;p&gt;Ето какво наистина работи за фотореалистичен output през 2026 г.:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Избери модел&lt;/strong&gt; -- Flux 2 за най-добър фотореализъм, Juggernaut XL за SDXL екосистемата&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Напиши prompt на естествен език&lt;/strong&gt;, който описва какво виждаш&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Генерирай при 1024x1024&lt;/strong&gt;, DPM++ 2M Karras, CFG 7-9, 25-30 steps&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Използвай ControlNet&lt;/strong&gt;, ако ти трябва конкретна композиция (depth или pose)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Итерирай върху prompt-а&lt;/strong&gt; -- генерирай 4-8 изображения, избери най-доброто&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Upscale&lt;/strong&gt; победителя до целевата ти резолюция&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Inpaint&lt;/strong&gt; проблемните области (ръце, очи, малки детайли)&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Това е същият workflow, независимо дали си в ComfyUI или A1111. Инструментите се различават, pipeline-ът -- не.&lt;/p&gt;
&lt;hr&gt;
&lt;h3&gt;Източници&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;Официална страница на Flux 2 model.&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 с ComfyUI и adoption от индустрията.&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;SD 3.0 deprecation и подробности за 3.5 release-а.&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;Хардуерно сравнение за генериране на изображения.&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;Обновена ControlNet документация за множество архитектури.&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;Модел за генериране в 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;Текущ пейзаж на моделите.&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 от Civitai.&lt;/em&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h3&gt;Свързани публикации&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; -- бърза справка за параметри, samplers и troubleshooting.&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[Stable Diffusion cheat sheet: troubleshooting и оптимизация]]></title><description><![CDATA[Обновено март 2026. Оригиналната версия на този cheat sheet беше написана за SD 1.5 през май 2023. Оттогава почти всичко се промени -- нови…]]></description><link>https://bdteo.com/bg/stable-difussion-cheat-sheet/</link><guid isPermaLink="false">https://bdteo.com/bg/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;Обновено март 2026.&lt;/strong&gt; Оригиналната версия на този cheat sheet беше написана за SD 1.5 през май 2023. Оттогава почти всичко се промени -- нови архитектури (SDXL, SD 3.5, Flux), нови UI-та (ComfyUI), нов хардуер (RTX 5090) и пълен обрат във философията за negative prompts. Това е актуалната версия.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Това е работният ми справочник за параметрите в Stable Diffusion. Не tutorial -- просто настройките, към които посягам, когато нещо не работи или когато искам да избутам качеството нагоре.&lt;/p&gt;
&lt;h2&gt;Кой модел да използваш&lt;/h2&gt;
&lt;p&gt;Това вече е първото решение и има по-голямо значение от всяко пипане на параметри.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Модел&lt;/th&gt;
&lt;th&gt;Най-добър за&lt;/th&gt;
&lt;th&gt;Резолюция&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;strong&gt;Flux 2&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Фотореализъм, следване на prompt-а&lt;/td&gt;
&lt;td&gt;1024x1024+&lt;/td&gt;
&lt;td&gt;Най-добрият open-weight модел за фотореализъм през 2026. Интегриран в Adobe Photoshop &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;Обща употреба&lt;/td&gt;
&lt;td&gt;1024x1024&lt;/td&gt;
&lt;td&gt;Огромна екосистема от fine-tune-и. 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;Топ качество (flagship моделът на Stability)&lt;/td&gt;
&lt;td&gt;1024x1024&lt;/td&gt;
&lt;td&gt;MMDiT архитектура. SD 3.0 беше deprecated през април 2025 &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;Скорост&lt;/td&gt;
&lt;td&gt;1024x1024&lt;/td&gt;
&lt;td&gt;Генериране в 2-8 стъпки. По-добро качество от Turbo при по-висока резолюция &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;Огромна библиотека от fine-tune-и, но постепенно излиза от употреба. SD 2.0/2.1 са официално deprecated&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Ако започваш начисто: &lt;strong&gt;Flux 2 за фотореализъм, SDXL за всичко останало.&lt;/strong&gt; SD 3.5 е добър, но екосистемата е по-малка.&lt;/p&gt;
&lt;h2&gt;Кой UI да използваш&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;UI&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;strong&gt;ComfyUI&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Power users. Node-based, по-добро управление на VRAM, 15% по-бърз, най-добра поддръжка за Flux. Индустриален стандарт за сериозна работа към 2025 &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;Начинаещи. По-прост интерфейс, огромна библиотека от extensions. Все още работи добре за SDXL&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;Генериране с един клик. Минимална конфигурация. Добър за бързи резултати&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Аз използвам ComfyUI. Learning curve-ът е по-стръмен (очаквай 10-20 часа, докато ти стане удобно), но само управлението на VRAM си струва -- пуска SDXL на 8GB там, където A1111 се срива.&lt;/p&gt;
&lt;h2&gt;Samplers&lt;/h2&gt;
&lt;p&gt;Спорът за samplers е общо взето приключил.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Go-to избори:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;DPM++ 2M Karras&lt;/strong&gt; -- най-доброто съотношение скорост-към-качество. Това ми е default-ът за почти всичко.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;DPM++ SDE Karras&lt;/strong&gt; -- малко по-добър при нисък брой стъпки. Добър, когато итерираш бързо.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Euler a&lt;/strong&gt; -- все още надежден. Повече разнообразие в outputs, добър за exploration.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Кога да смениш:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Липса на разнообразие в outputs? Пробвай DPM++ SDE или Euler a.&lt;/li&gt;
&lt;li&gt;Artifacts или oversaturation? Пробвай DPM++ 2M Karras или plain Euler.&lt;/li&gt;
&lt;li&gt;Трябва ти скорост преди всичко? Euler a или DPM++ 2M (non-Karras).&lt;/li&gt;
&lt;li&gt;Искаш максимално качество? DPM++ 3M SDE Karras или UniPC.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Брой стъпки:&lt;/strong&gt; 20-30 стъпки за повечето samplers. Lightning моделите имат нужда само от 2-8.&lt;/p&gt;
&lt;h2&gt;CFG (Classifier Free Guidance)&lt;/h2&gt;
&lt;p&gt;Колко стриктно моделът следва prompt-а ти спрямо собствената си интерпретация.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Диапазон&lt;/th&gt;
&lt;th&gt;Ефект&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;Много творчески, свободна интерпретация. Често incoherent&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;Добър баланс за повечето работа&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;Силно следване на prompt-а. Sweet spot за SDXL фотореализъм&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;10-15&lt;/td&gt;
&lt;td&gt;Риск от artifacts и преготвени цветове&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;15+&lt;/td&gt;
&lt;td&gt;Почти винаги прекалено. Artifacts гарантирани&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;Бележка:&lt;/strong&gt; SD 3.5 използва различен guidance механизъм. Концепцията за CFG все още важи, но скалата се държи различно -- започни по-ниско (3-5) и настройвай оттам.&lt;/p&gt;
&lt;h2&gt;Резолюция&lt;/h2&gt;
&lt;p&gt;Дните на 512x512 свършиха.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Модел&lt;/th&gt;
&lt;th&gt;Нативна резолюция&lt;/th&gt;
&lt;th&gt;Практичен диапазон&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 до 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 (стандарт), 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 е възможно на high-end GPU-та&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Да минеш над нативната резолюция носи риск от artifacts и проблеми с композицията. Използвай hi-res fix или upscaling, вместо да генерираш директно на 2048x2048.&lt;/p&gt;
&lt;h2&gt;Clip Skip&lt;/h2&gt;
&lt;p&gt;По-малко релевантен е, отколкото беше.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;SD 1.5:&lt;/strong&gt; Clip skip 1-2 има голямо значение. Anime моделите често използват clip skip 2.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;SDXL:&lt;/strong&gt; Използва dual text encoders (CLIP + OpenCLIP). Clip skip до голяма степен се игнорира -- архитектурата го обработва различно.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;SD 3.5 / Flux:&lt;/strong&gt; Не е приложимо по същия начин. Тези модели използват transformer-based text encoding.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Ако си на SDXL или по-ново: не се тревожи за clip skip. Ако си на SD 1.5: дръж го на 1 за фотореализъм, 2 за anime.&lt;/p&gt;
&lt;h2&gt;Negative Prompts&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Философията се обърна.&lt;/strong&gt; През 2023 съветът беше да използваш дълги списъци с negative prompts. През 2026 консенсусът е: &lt;strong&gt;започни с нищо и добавяй само това, което ти трябва за поправка.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Защо промяната:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;SDXL и Flux разбират естествен език много по-добре от SD 1.5&lt;/li&gt;
&lt;li&gt;Дългите negative prompts всъщност могат да &lt;em&gt;ограничат креативността&lt;/em&gt; и да влошат резултатите&lt;/li&gt;
&lt;li&gt;&quot;bad anatomy&quot; е твърде неясно, за да е полезно. &quot;ugly&quot; не работи, защото SD не е трениран върху изображения, етикетирани като &quot;ugly&quot;&lt;/li&gt;
&lt;li&gt;Някои модели се представят демонстративно по-зле с дълги negatives &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;Актуалният подход:&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Първо генерирай без никакъв negative prompt.&lt;/li&gt;
&lt;li&gt;Ако видиш конкретен проблем (extra fingers, blurry background), добави targeted negative за него.&lt;/li&gt;
&lt;li&gt;Използвай emphasis weighting: &lt;code class=&quot;language-text&quot;&gt;(blurry:1.3)&lt;/code&gt; вместо само &lt;code class=&quot;language-text&quot;&gt;blurry&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Дръж го кратко -- максимум 5-10 термина.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;GPU бърз справочник&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;Добър за&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, basic 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, малко 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;Всичко. Работната машина&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;Всичко, включително 4K и batch generation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;8GB карти&lt;/td&gt;
&lt;td&gt;8GB&lt;/td&gt;
&lt;td&gt;Минимално жизнеспособно. ComfyUI помага с VRAM management&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Границата от 24GB е мястото, където нещата стават удобни за SDXL и Flux без постоянно жонглиране с VRAM.&lt;/p&gt;
&lt;h2&gt;Troubleshooting бързи поправки&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Проблем&lt;/th&gt;
&lt;th&gt;Пробвай&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Blurry output&lt;/td&gt;
&lt;td&gt;Увеличи steps. Провери дали резолюцията съвпада с native res на модела&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Extra fingers/limbs&lt;/td&gt;
&lt;td&gt;Добави &lt;code class=&quot;language-text&quot;&gt;extra fingers, extra limbs&lt;/code&gt; към negative prompt. Или използвай ControlNet&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Oversaturated colors&lt;/td&gt;
&lt;td&gt;Намали CFG. Смени на DPM++ 2M Karras&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Композицията е грешна&lt;/td&gt;
&lt;td&gt;Използвай ControlNet (depth, canny, pose), вместо да се бориш с prompt-а&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Generation is slow&lt;/td&gt;
&lt;td&gt;Използвай Lightning model, намали steps, използвай ComfyUI за по-добър VRAM&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Out of VRAM&lt;/td&gt;
&lt;td&gt;Смени на ComfyUI, намали batch size, използвай fp16&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr&gt;
&lt;h3&gt;Източници&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;Покритието на NVIDIA за Flux 2 с 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 deprecation и release-ът на 3.5.&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;Генериране в 2-8 стъпки на 1024px.&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 и възможности.&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;Обновен guide за минималистичната философия на negative prompts.&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;Сравнение и избор на samplers.&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;Актуалният пейзаж на моделите.&lt;/em&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h3&gt;Свързани публикации&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;/pushing-the-stable-diffussion-limits/&quot;&gt;Stable Diffusion фотореализъм: guide за настройки и GPU лимити&lt;/a&gt; -- дълбоко гмуркане в постигането на фотореалистични резултати с актуалните модели.&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[За мен]]></title><description><![CDATA[Страниците „За мен“ са странно нещо за писане. Накрая започваш да описваш себе си като непознат човек, а аз съм подозрителен към това…]]></description><link>https://bdteo.com/bg/about/</link><guid isPermaLink="false">https://bdteo.com/bg/about/</guid><pubDate>Thu, 04 May 2023 22:12:03 GMT</pubDate><content:encoded>&lt;p&gt;Страниците „За мен“ са странно нещо за писане. Накрая започваш да описваш себе си като непознат човек, а аз съм подозрителен към това.&lt;/p&gt;
&lt;p&gt;Затова, направо. Аз съм Борис. Пиша софтуер от малко повече от четиринайсет години. Работата плаща наема, храни кучетата и ми дава проблем за дъвчене почти всяка сутрин — което, честно казано, е повече, отколкото много работи успяват да дадат. Тези дни съм backend половината на малък продуктов екип, някъде между Laravel, Docker и Kubernetes, най-често притеснен за опашки, които трябва да се изпразват по-бързо, отколкото се пълнят, и индекси, които растат по-бързо, отколкото ми се иска.&lt;/p&gt;
&lt;p&gt;Преди софтуера учих математика в университета. Никога съвсем не спрях. Математиката е мястото, където отивам, когато светът стане прекалено шумен. Особено теорията на числата — има нещо честно в простото число, което не намирам на много други места. Голяма част от това, което пиша тук, започва оттам. Малък математически сърбеж. Наполовина разбрана медицинска статия. Френско изречение, което трябваше да прочета четири пъти. После дръпвам нишката и гледам какво още е вързано за нея.&lt;/p&gt;
&lt;p&gt;Блогът е мястото, където се случва това дърпане. Нищо тук не е експертен труд. Чета медицина, която нямам работа да чета. Чета български и френски романи и се преструвам, че смогвам. Уча немски бавно, най-вече защото граматиката ме забавлява. Понякога пиша C++ на ръка. Не за работа — просто защото дисциплината сама по себе си е нещо. Есетата излизат от всичко това — тихи опити да запиша какво съм забелязал, преди да го забравя.&lt;/p&gt;
&lt;p&gt;Живея в София. Две спасени кучета — Кожухка и Еклер — се грижат да не се вземам прекалено насериозно. Преди са се проваляли. Продължават да опитват.&lt;/p&gt;
&lt;p&gt;Това е почти всичко. Поддържам и малка freelance табела под името Percepticus, за работа, която идва от уста на уста. Иначе текстовете са тук, а за останалото ми е добре да мълча.&lt;/p&gt;</content:encoded></item></channel></rss>