<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
  xmlns:content="http://purl.org/rss/1.0/modules/content/"
  xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Oleksii Mazurenko — Blog</title>
    <link>https://oleksiimazurenko.dev/kk/blog</link>
    <description>Technical articles about web development, performance optimization, and developer tools.</description>
    <language>kk</language>
    <lastBuildDate>Tue, 30 Jun 2026 00:00:00 GMT</lastBuildDate>
    <atom:link href="https://oleksiimazurenko.dev/kk/blog/feed.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>Тақталарымды Miro-дан Obsidian-ға шығарамын</title>
      <link>https://oleksiimazurenko.dev/kk/blog/migrate-miro-board-to-obsidian</link>
      <guid isPermaLink="true">https://oleksiimazurenko.dev/kk/blog/migrate-miro-board-to-obsidian</guid>
      <description>Miro-дағы Export түймесі саған PNG не PDF береді — бұл деректің фотосуреті, деректің өзі емес. Егер тақталарың жылдар бойы жиналған load-bearing ойлау карталары болса, олардың орны Obsidian-да: editable, searchable, жергілікті vault ішінде. Тақталарды Miro-дан REST API арқылы қалай шығарып, Excalidraw / Obsidian Canvas-қа түрлендіргенім — позицияларды, түстерді, қаріптерді, curved connectors-ты, mindmap nodes-ты сақтай отырып — ал сабақ Miro-дан әлдеқайда кең: кез келген SaaS-ты алғаш ашпай тұрып exit plan-мен қарсы алу керек.</description>
      <content:encoded><![CDATA[<p>Браузерде тұратын кез келген құрал сені өз UI-ының кепіліне айналдырады. Әсіресе оған уақытыңды, карталарыңды, boards-ыңды құйғаннан кейін — сосын өзіңнің map of thinking деген нәрсең біреудің серверінде, олардың бетін ашпай көрінбейтін форматта жатқанын кенет түсінесің.</p>
<p>Менде бірнеше Miro тақтасы бар, олар әлдеқашан load-bearing болып кеткен — жылдар бойы жиналған рефлексиялар, жоспарлар, ойлау карталары. Export түймесі саған PNG не PDF береді. Бұл — дерек емес, деректің фотосуреті.</p>
<h2>Неге оларды сыртқа шығару керек</h2>
<p>Vendor lock-in — теориялық мәселе емес. Ол нақты: өз деректеріңді API арқылы жүктеп алу үшін Expert tier-ге upgrade жасауың керек. Яғни, өзіңдікі болып тұрған нәрсені қайтарып алуға рұқсат үшін ақы төлейсің.</p>
<p>Мен әлдеқашан Obsidian-first принципімен өмір сүремін: маңызды нәрсенің бәрі жергілікті vault ішінде, git-ке салуға, grep жасауға және он жылдан кейін де оқуға болатын plain files түрінде жатады. Осындай форматқа экспорт бермейтін құрал — тәуелділік үзуге келмейтін дәрежеге жетпей тұрып кетуің керек құрал.</p>
<h2>Неге Export сені құтқармайды</h2>
<p>Miro-дағы Export түймесі саған PNG не PDF береді. Бұл — raster, құрылымы жоғалған. Box ішіндегі мәтінді өзгерте алмайсың. Shape-ті сүйреп жылжыта алмайсың. Жазуды grep жасай алмайсың. Бұл — тақтаның қазасы туралы хабарлама, тақтаның өзі емес.</p>
<p>Obsidian-да vault ішінде тұратын екі балама бар: Excalidraw plugin және нативті Canvas. Екеуі де — JSON. Екеуі де — editable. Екеуі де — searchable. Екеуі де жазбаларыңның қасында vault ішінде жатады, ал мәтінді тікелей Obsidian ішінен grep жасай аласың.</p>
<h2>&apos;Export to Obsidian&apos; түймесі жоқ кезде оларды қалай шығарасың</h2>
<p>UI-дағы Export саған керегін бермесе — API-ге кіресің. Miro-да REST API бар: ол тақтадағы әрбір shape-тің JSON сипаттамасын қайтарады. Excalidraw мен Obsidian Canvas — екеуі де open-format JSON. Олардың арасында — mapping.</p>
<p>Мен екі Python скрипт жаздым. <code>miro_to_excalidraw.py</code> және <code>miro_to_canvas.py</code>. Standard library only — нөл deps. Оларды token мен board ID-мен іске қосасың, файл аласың, оны vault-қа тастайсың.</p>
<h2>Түрлендіруден кейін не сақталды</h2>
<p>Мақсат тек shapes-ті көшіріп әкелу емес еді — тақта Miro-дағыдай оқылатындай етіп визуалды сақтап қалу еді.</p>
<ul><li><strong>Позициялар мен өлшемдер</strong> — әрбір shape-тің координаттары 1:1</li><li><strong>Түстер</strong> fill opacity-мен (40% сары түс 40% сары болып қала береді)</li><li><strong>Borders</strong> — түс, ені, стилі (solid / dashed)</li><li><strong>Қаріптер мен alignment</strong> — monospace monospace болып қалады, center center болып қалады</li><li><strong>Connectors</strong> — көрсеткілер Miro бекіткен нүктелердің дәл өзінде тұрады</li><li><strong>Curved connectors</strong> — түзу сызықтардың орнына bow доғалары</li><li><strong>Images</strong> файлдың ішіне ендірілген (сыртқы URL емес)</li><li><strong>Mindmap nodes</strong> — мәтін experimental Mindmap API арқылы шығарылған</li><li><strong>Z-ordering</strong> — overlap шатаспайды</li></ul>
<h2>Қай жерде қолайсыз болды</h2>
<p>Miro API-де бірнеше олқылық бар, оларды reverse engineering арқылы айналып өтуге тура келді.</p>
<ul><li><strong>Curved connectors</strong> — Miro control points-ты API арқылы ашпайды. Сондықтан дәл түпнұсқа қисықтың орнына мен bow синтездеймін — екі endpoint арасындағы тұрақты радиусты доға. Визуалды жағынан жақын, mathematically identical емес.</li><li><strong>Z-index</strong> — Miro API z өрісін мүлде қайтармайды. Қай shape үстінде, қайсысы астында тұруы керек? Эвристика: ауданы үлкенірек = артқа тереңірек. Бұл жұмыс істейді, өйткені нақты тақталарда үлкен контейнерлер әдетте ұсақ элементтердің астында отырады.</li><li><strong>Mindmap</strong> — mindmap nodes мәтіні кәдімгі items API арқылы қолжетімсіз. Experimental Mindmap endpoint-ке бөлек call керек. Filled nodes boxes-қа айналады, unfilled — plain text-ке.</li></ul>
<h2>Excalidraw vs Canvas — қалай таңдау керек</h2>
<p>Егер сен drawings стилінде ойласаң — sketchy lines, hand-drawn feel, freeform композициялар — <strong>Excalidraw</strong> таңда. Excalidraw plugin файлды whiteboards-та көрген дәл сол эстетикалық стильде рендерлейді.</p>
<p>Егер тақталарың негізінен <em>көрсеткілермен жалғанған, мәтіні бар тіктөртбұрыштар</em> болса және сен Obsidian-да second brain ретінде өмір сүрсең — <strong>Canvas</strong> таңда. Canvas нативті, бір басқанда ашылады және vault-тың backlinks пен tags-ымен тұтасып тұр.</p>
<p>Кейбір тақталарды Excalidraw-ға (visual style маңызды болғанда), басқаларын Canvas-қа (жылдамдық пен нативті feel маңызды болғанда) түрлендіремін. Repo екі форматты да қолдайды — іске қосу кезінде таңдайсың.</p>
<h2>Қорытынды</h2>
<p>Python 3, standard library only. pip install жоқ, virtualenv жоқ, node_modules жоқ. <code>python3 miro_to_excalidraw.py 'BOARD_ID' 'output.excalidraw'</code> — сосын файл сенің қолыңда.</p>
<p>Repo: <a href="https://github.com/oleksiimazurenko/miro-to-obsidian">github.com/oleksiimazurenko/miro-to-obsidian</a>. MIT лицензиясы. PR-лар құпталады — әсіресе мен әлі кездестірмеген edge case-тер үшін.</p>
<h2>Сабақ Miro-дан әлдеқайда кең</h2>
<p>SaaS lock-in — есірткі. Алғашқы айлар тегін. Алты айдан кейін — әдет. Бір жылдан кейін деректерің тек олардың UI-ы оқи алатын форматта тұрады. Екі жылдан кейін — өзіңдікі болып тұрған нәрсені қазып алу үшін Expert tier-ге ақы төлейсің.</p>
<p>Қорытынды «SaaS-ты қолданба» емес. Қорытынды мынау: load-bearing болуға жол берген әрбір құралға <strong>оны алғаш ашпай тұрып exit plan</strong> керек. «Керек болғанда бұл жерден қалай шығамын» деген сұрақ «UI қаншалықты жайлы» дегенмен бірдей салмақты. Plain-text экспорты немесе ашық API-і жоқ құрал — екі жылдан кейін төлейтін жасырын баға белгісі бар құрал.</p>
<p>Obsidian-first Obsidian мінсіз болғандықтан емес. Ал plain markdown файлдары Obsidian-ның өзінен ұзақ өмір сүретін формат болғандықтан. Дәл солай, жалаң Excalidraw JSON файлы plugin-нің өзінен ұзақ өмір сүреді. Дәл солай, бұл Python скрипті Miro-дан ұзақ өмір сүреді.</p>]]></content:encoded>
      <pubDate>Tue, 30 Jun 2026 00:00:00 GMT</pubDate>
      <category>obsidian</category>
      <category>miro</category>
      <category>automation</category>
      <category>tools</category>
      <category>open-source</category>
    </item>
    <item>
      <title>CRAFT: кәсіби шеберлігіңнен айырылмай AI пайдалану әдісі</title>
      <link>https://oleksiimazurenko.dev/kk/blog/craft-method-ai-without-deskilling</link>
      <guid isPermaLink="true">https://oleksiimazurenko.dev/kk/blog/craft-method-ai-without-deskilling</guid>
      <description>Емсіз диагноз — жай ғана мазасыздық. AI салдарынан дағдыдан айырылу туралы жазбадан кейін, міне, рецепт: CRAFT — бір аксиома астындағы бес қағида (Challenge First, Review Adversarially, Ask Why Not What, Friction on Purpose, Test Your Understanding): AI сен меңгергенді істейді, сен әлі үйреніп жүргеніңді істейсің. AI-ды күнде пайдаланып, дағдыңнан байқаусызда айырылмаудың практикалық әрі есте жақсы сақталатын әдісі.</description>
      <content:encoded><![CDATA[<p>Өткен жазбамда дертке диагноз қойдым: AI бұрын шындықтың өзі сен үшін қосып отыратын мәжбүрлі кері байланыс тізбегін үнсіз ажыратып тастады — компилятор қатесін, құлаған тестті, рецензенттің үнсіз көзқарасын. Сені оқуға <em>мәжбүрлеген</em> қысым жоғалды, ал оңай жол енді табыстың бетпердесін киіп алды. Бұл жазба — рецепт.</p>
<p>Кеңестердің ретсіз тізімі емес — олар шаршаған сәтіңде-ақ буланып ұшып кетеді. Қысым астында есіңе түсіре алатын, аты бар канон — нысанға бағдарланған дизайн үшін SOLID қалай жұмыс істесе, солай. Мен оны <strong>CRAFT</strong> деп атаймын: бес қағида, әрқайсысына бір әріп, бәрі бір аксиоманың астында. Атаудың өзі — бүкіл мән: кодтау қолөнер болып қала береді, ал AI — онсыз да жұмысты атқара алатын адамның қолындағы жай ғана құрал.</p>
<h2>Аксиома: жылжымалы сызық</h2>
<p>Бес әріптің алдында олардың бәрін билейтін бір ереже тұр — SOLID-тің жүрегінде «жалғыз жауапкершілік» тұрғаны сияқты:</p>
<blockquote>AI сен әлдеқашан меңгергенді істейді. Сен әлі үйреніп жүргеніңді істейсің. Сызық сен өскен сайын жылжып отырады.</blockquote>
<p>Меңгерілген бөлікті AI-ға бер — сонда жылдамырақ боласың, бұл таза ұтыс. Әлі қалыптастырып жүрген бөлігіңді AI-ға берсең — оны мүлде қалыптастырмайсың; өзіңе тіпті қажет болмаған жылдамдық үшін шеберлікпен төлейсің. Төмендегі әр қағида — сол сызық бүгін сен үшін қай жерде тұрғанына адал болып қалудың амалы ғана.</p>
<h2>CRAFT — бес қағида, бір қысқарту</h2>
<p>Жоғарыдан төмен қарай оқығанда, әріптер — жұмыс ағыны: алдымен өзіңе сын таста, AI бергенді өшпенділікпен тексер, <em>не</em> емес, <em>неге</em> деп сұра, әдейі кедергі жаса және шынымен түсінгеніңді тексер.</p>
<h3>C — Challenge First</h3>
<p><strong>Қағида.</strong> AI-ды ашпас бұрын, есепті 5–15 минут өзің шешіп көр. Беттегі бірінші шешім <em>сенікі</em> болуы керек — тіпті олқы болса да. AI сені <strong>бөгеуден шығару</strong> үшін келеді, ешқашан сенің орныңа <strong>бастау</strong> үшін емес.</p>
<p><strong>Неге.</strong> Ойша модельді дәл осы арпалыс құрады — нейрондар сен тұрып қалғанда тұтанады, дайын жауапты оқып отырғанда емес. Бұл — бұрын шындық тегін жасайтын дәл сол кедергі; енді оны өзің жоспарлауың керек.</p>
<h3>R — Review Adversarially</h3>
<p><strong>Қағида.</strong> Кодыңды AI тексермейді — AI-дың кодын <em>сен</em> тексересің. Әр жасалған жолды багты, шеткі жағдайды, қауіпсіздік саңылауын аңдып оқы.</p>
<p><strong>Неге.</strong> Пайымдау — инженериядағы ең төзімді дағды әрі модельдер күшейген сайын құндырақ бола түсетін жалғыз дағды. AI-ды тексеру оны қайрайды және модельдер жасауды жақсы көретін сенімді қателерді ұстайды. Дифті оқымай қабылдау екеуін бірден бұзады.</p>
<h3>A — Ask Why, Not What</h3>
<p><strong>Қағида.</strong> AI-ды сенің орныңа жазатын жасырын автор ретінде емес, ұстаз ретінде пайдалан. Дайын үзіндіні ғана емес — пайымдауды, баламаларды, ымыраларды сұра.</p>
<p><strong>Неге.</strong> «Не» саған балық береді; «неге» қармақ береді. «Кодты бер» артыңда ештеңе қалдырмайды. «Неге басқа емес, осы тәсіл, әрі бос кірісте не бұзылады?» деген сені бұрынғыдан бір саты ақылдырақ қалдырады.</p>
<h3>F — Friction on Purpose</h3>
<p><strong>Қағида.</strong> Шындық енді сені күш салуға мәжбүрлемейді, сондықтан қарсылықты өзің қайта қос. Тұрақты түрде <strong>AI-сыз жаттығулар</strong> жаса — көмекшісіз, нөлден бастап бірдеңе құрастыр. Кодты қоюдың орнына қайта тер. AI-ды жауып, ол саған жаңа ғана үйреткенді өзің қайта жаңғырт.</p>
<p><strong>Неге.</strong> Дағды тек қажетті қиындық арқылы бекиді. Көшіріп-қою жадта із қалдырмайды; сол жолдарды баяу қайта теру — қалдырады. Кедергі жылдамдыққа салынған салық сияқты сезіледі — шын мәнінде бұл дағдыны тым болмаса сақтап қалудың құны.</p>
<h3>T — Test Your Understanding</h3>
<p><strong>Қағида.</strong> Басты қақпа — бір ғана сұрақ: <em>мен мұны басқа адамға жол-жолымен түсіндіре алар ма едім?</em> Егер жоқ болса — код саған тиесілі емес, оны шығармайсың. Іске қоспас бұрын нәтижесін болжа; әдейі бұзып, қайта жөнде.</p>
<p><strong>Неге.</strong> «Жұмыс істейді» деген «түсінемін» деген емес — дәл осы алшақтық құзыреттілік елесі болып табылады, әрі дағдыдан айырылу дәл осы жерде жасырынады. Өз түсінігіңді тексеру — бір күні құралсыз жұмыс істей алмайтын күйде оянудан сақтайтын ең арзан сақтандыру.</p>
<h2>CRAFT қысқаша</h2>
<table><thead><tr><th>Қағида</th><th>Тұзақ</th><th>Әдет</th></tr></thead><tbody><tr><td>&lt;strong&gt;C&lt;/strong&gt; · Challenge First</td><td>Бос бетке промпт жазу</td><td>Алдымен 5–15 мин өзің көр</td></tr><tr><td>&lt;strong&gt;R&lt;/strong&gt; · Review Adversarially</td><td>Дифті оқымай қабылдау</td><td>AI шығысынан багты ізде</td></tr><tr><td>&lt;strong&gt;A&lt;/strong&gt; · Ask Why, Not What</td><td>«Жай ғана кодты бер»</td><td>«Тәсіл мен ымыраларды түсіндір»</td></tr><tr><td>&lt;strong&gt;F&lt;/strong&gt; · Friction on Purpose</td><td>Бәрін көшіріп-қою</td><td>AI-сыз жаттығулар; қоймай, қайта тер</td></tr><tr><td>&lt;strong&gt;T&lt;/strong&gt; · Test Your Understanding</td><td>Түсіндіре алмайтын кодты шығару</td><td>«Мұны жол-жолымен түсіндіре алар ма едім?»</td></tr></tbody></table>
<h2>Өзіңді тексер: өсіп жатырсың ба, әлде тозып жатырсың ба?</h2>
<p>Қай бағытта бара жатқаныңды білу үшін зерттеу қажет емес. <strong>Мына жағдайда тозып жатырсың:</strong></p>
<ul><li>AI-сыз сол нәрсенің қарапайым нұсқасын жаза алмайсың.</li><li>Біреу сұраса, түсіндіре алмайтын кодты қоясың.</li><li>Көмекші сөндірулі күйде жұмыс істеу ойынан-ақ мазасызданасың.</li><li>AI шығысын оқуды қойып, жай ғана қабылдай саласың.</li><li>Үзіндіні іске қоспас бұрын оның не істейтінін болжай алмайсың.</li></ul>
<p><strong>Мына жағдайда өсіп жатырсың:</strong></p>
<ul><li>AI сені шынымен түсінетін нәрселерде жылдамдатады.</li><li>Модельдің қателерін жүйелі түрде ұстайсың.</li><li>Онсыз да әлі жұмыс істей аласың — баяуырақ, бірақ істей аласың.</li><li>Оны жаңа тұжырымды үйрену үшін пайдаланып, сосын сол тұжырымды көмексіз қайта жаңғыртасың.</li><li>Нені беріп, нені өзіңде қалдыратыныңды саналы түрде шешесің.</li></ul>
<h2>Егер бір-ақ әріп алсаң</h2>
<p><strong>T</strong>-ні ал. Түсіндірілу қақпасы — <em>мұны жол-жолымен түсіндіре алар ма едім?</em> — бүкіл әдістемедегі ең қарапайым әдет, әрі жалғыз өзі тозудың шамамен 80%-ын ұстайды. CRAFT-тағы қалғанының бәрі сол қақпадан адал өтуді жеңілдете түседі.</p>
<h2>Қорытынды</h2>
<p>Оқуда түбегейлі ештеңе өзгерген жоқ. Саналы түсіну әрқашан маңызды болды; жаттап алу әрқашан нашар алмастырғыш болды. Өзгергені — орта сол айырманы сенің орныңа енді мәжбүрлеп ұстамайды. CRAFT — оны өзіңе-өзің мәжбүрлеп ұстатудың амалы ғана.</p>
<blockquote>Ойын ережелері сол қалпында. Тек ойын қатерлірек болды — оңай жол енді табыстың бетпердесін киіп алды.</blockquote>
<p>Егер осының бәрінің артындағы диагнозды әлі оқымаған болсаң, AI мен жоғалған кері байланыс туралы алдыңғы жазбадан баста. Ол — дерт туралы еді; бұл — ем.</p>]]></content:encoded>
      <pubDate>Tue, 23 Jun 2026 00:00:00 GMT</pubDate>
      <category>ai</category>
      <category>learning</category>
      <category>career</category>
      <category>methodology</category>
    </item>
    <item>
      <title>AI өшірген тыныштық</title>
      <link>https://oleksiimazurenko.dev/kk/blog/ai-deskilling-and-feedback-loop</link>
      <guid isPermaLink="true">https://oleksiimazurenko.dev/kk/blog/ai-deskilling-and-feedback-loop</guid>
      <description>AI бар кезде бағдарламалауды үйренудің мәні бар ма? Даурыққан жауап екі жағынан да қате. 2025–2026 дәлелдер базасымен (Nature, Anthropic, AI &amp; Society) сұрақ &quot;AI ма, жоқ па&quot; емес, қай дағдыларды AI-ға беріп, қайсысын өзіңде қалдыру керек — әрі бір нәрсе шынымен өзгерді: бұрын шындық сені үйкеліс арқылы үйренуге мәжбүрлейтін, AI бұл қысымды үнсіз өшірді.</description>
      <content:encoded><![CDATA[<p>"AI бар кезде бағдарламалауды үйренудің мәні бар ма" деген талқы қазір екі апта сайын нөлден қайта басталады. Мен бұған айлар бойы бас қатырдым — әуелі теориялық сұрақ ретінде, кейін "неден бастасам" деп сұраған кіші маманға не айтарыңды шешетін сұрақ ретінде. Дәлелдер базасы пайда болды, сондықтан бұл енді жай пікірге қарсы пікір емес. Ашығын айтқанда, даурыққан екі шеткі көзқарастың екеуі де қателеседі.</p>
<p>Бұл "үйрену керек пе, жоқ па" деген емес. Бұл <strong>нақты нені</strong> AI-ға беріп, нені өзіңде қалдыратының туралы. Әрі AI шынымен өзгерткен бір нәрсе туралы — "бәрі аударылып түсті" деген бағытта емес, әлдеқайда нәзік бағытта: <strong>мәжбүрлі кері байланыс жоғалды</strong>, оны бұрын шындықтың өзі сенің орныңа істейтін еді.</p>
<h2>Екі шеткі көзқарас — екеуі де біржақты</h2>
<p>Алдымен әр талқыда қылтиятын екі даурыққан лагерьден үстелді тазалап алайық:</p>
<ul><li><strong>1-лагерь:</strong> "Тілдерді үйренудің керегі жоқ — AI жазып береді". Ең қатты айқайлайтындар — "30 күнде AI-мен бағдарламашы бол" деген буткемптерді сататындар.</li><li><strong>2-лагерь:</strong> "Бәрі топастанады, бірер жылда мамандар қалмайды". Ең қатты айқайлайтындар — кіші мамандардың қаншалық тез өнімді көрінетінінің алдында өз дәрменсіздігінен сәл сескенетін тәжірибелілер.</li></ul>
<p>Жалған дихотомия. Түбінде іргелі ештеңе өзгерген жоқ: әрдайымғыдай, құрғақ жаттау емес, <strong>саналы түсіну</strong> бағаланады. AI тек шынымен білетіндер мен қалғандардың арасындағы алшақтықты тереңдетеді — әрі мұны бұрынғыдан жылдам істейді.</p>
<h2>Дәлелдер базасы не дейді</h2>
<p>Бұл енді "маған солай көрінеді" емес. 2025–2026 жылдары нақты өлшемдер пайда болды:</p>
<ul><li><strong>Nature (2026 маусым), "Is AI ruining our skills?"</strong> — AI-ға шамадан тыс сүйену дағдыларды тоздырады. Тек бағдарламашыларда емес — дәрігерлерде де.</li><li><strong>Anthropic RCT</strong>, 52 инженерге: AI-көмекші <strong>шеберліктің қалыптасуын ~17%-ға төмендетеді</strong>. Нәзік тұсы — тапсырманы орындау жылдамдығы емес, дағдының <em>қалыптасуы</em> зардап шегеді. Яғни қысқа мерзімде жылдамсың, ұзақ мерзімде — нашарсың.</li><li><strong>AI &amp; Society</strong>: deskilling — дайын шешімі жоқ <strong>құрылымдық</strong> мәселе. "AI жаман" емес, "құрал оқу ортасын өзгертті".</li><li><strong>TechCrunch</strong>: инженерлер AI-сыз жұмыс істеуден бас тартады, өйткені онсыз енді істей алмайды. Тақырыбы: "бұл кейін кері соғуы мүмкін".</li></ul>
<p>Сарапшы K. Crowston-ның қорытындысы (Nature келтіреді) — бұл "AI ма, жоқ па" емес, мынау: <strong>қай дағдыларды өзіңде қалдырып, қайсысын беретініңді саналы түрде шеш</strong>. Постың қалған бөлігі осы сөйлемге сүйенеді.</p>
<h2>Нені беру, нені қалдыру</h2>
<p>Нақтырақ — шекара мына жерден өтеді:</p>
<table><thead><tr><th>AI-ға беруге болады</th><th>Өзіңде қалдыру керек</th></tr></thead><tbody><tr><td>Синтаксисті не API қолтаңбасын еске түсіру</td><td>Кодты &lt;strong&gt;оқып, түсіну&lt;/strong&gt;</td></tr><tr><td>Boilerplate-ты нөлден жазу</td><td>&lt;strong&gt;Бағалау&lt;/strong&gt;: дұрыс па? қауіпсіз бе? баг бар ма?</td></tr><tr><td>Мысалды тез табу</td><td>Іргетас: жүйелер, деректер, желі қалай жұмыс істейді</td></tr></tbody></table>
<p><strong>Оқи алмайтын нәрсеңе review жасай алмайсың.</strong> Сондықтан "тілді білмей түсіну" — елес; бұл AI-ға соқыр сенім. Әрі дәл осы жер "AI пайдаланатын маман" мен "AI операторының" арасындағы өту нүктесі. Біріншісі AI-ды басқарады; екіншісін AI басқарады.</p>
<p>Тағы да үнемі шатастырылатын айырма: <strong>дағдыны жоғалту</strong> ≠ <strong>дағдының қалыптаспауы</strong>. Кенет AI-ға сүйене бастаған тәжірибелі маман мықты күйінде қалады — оның дүние моделі бұрыннан бар. Бірінші күннен 100% AI-ға арқа сүйеген жаңадан бастаушы бұл модельді ешқашан құрмайды. Бұл — салдары әртүрлі, әртүрлі мәселелер.</p>
<h2>Қалай дұрыс үйрену керек — және неге, түбінде, ештеңе өзгермеді</h2>
<p>Үйрену әрі қарай да керек — тек <strong>дұрыс</strong>. Терминдер мен командаларды құрғақ жаттау емес, логиканы түсініп, оның тұтастай қалай жұмыс істейтінін сезіну. Құндылық "есте сақтауда" емес, "түсіну → қолдана білу → бағалауда".</p>
<p>Ең бастысы: <strong>бұл AI-ға дейін де әрдайым болып келген нәрсенің дәл өзі.</strong> Әрқашан құрғақ жаттағандар емес, маман ретінде шынымен білетіндер бағаланды. AI ештеңені аударып тастаған жоқ — ол тек жалқау жолды жеңілдетеді (енді түсінбей-ақ "істеуге" болады), сондықтан осы екі типтің арасындағы алшақтық тек тереңдей түседі.</p>
<blockquote>Саналы үйренген — сол күйінде қалады. Жалқау болған — жалқау күйінде қалады. AI бұл бөліністі тудырған жоқ — тек оны жарыққа шығарды.</blockquote>
<h3>Қарапайым мысалдармен тексер</h3>
<ul><li><strong>Математика:</strong> көбейту кестесін жатқа білу әлі "математиканы білу" емес. Калькулятор бағанмен есептеуді өлтірді, бірақ көбейту дегеннің не екенін және оны қашан қолдану керегін түсіну қажеттілігін өлтірген жоқ.</li><li><strong>Телефондар:</strong> нөмірлерді енді басыңда ұстамайсың — телефон есте сақтайды. Бірақ кімге, не үшін қоңырау шалатыныңды білуің керек. Жадты бердің — пайымды қалдырдың.</li><li><strong>GPS:</strong> бағыттарды басыңда ұстамайсың. Бірақ қайда бара жатқаныңды түсінуің керек — және GPS сені бір ақымақ айналма жолмен апарып жатқанын байқауың керек.</li></ul>
<p>Үшеуінде де машина <strong>механикалық жадты</strong> алды, <strong>түсіну мен пайымды</strong> емес — әрі әрдайым солай болған. AI — сол нәрсенің келесі итерациясы, тек енді тапсыруға болатын ауқым кеңірек.</p>
<h3>Синтаксис — қосымша өнім, мақсат емес</h3>
<p>Бұл "синтаксисті білудің керегі жоқ" дегенді білдірмейді. Синтаксисті бәрібір игересің — бірақ <strong>түсіну мен тәжірибенің қосымша өнімі ретінде, мақсат ретінде емес</strong>. Сен <code>%d</code>-ны флешкартамен жаттаған жоқсың ғой — format-string логикасын түсіндің де, ол өзінен-өзі есте қалды. Қалыпты оқу осылай жұмыс істейді: түсінесің → қолданасың → синтаксис өзі орнығады. AI, оны дұрыс пайдалансаң, тек осы циклды жеделдетеді.</p>
<h2>Бірақ бір нәрсе шынымен өзгерді: мәжбүрлі кері байланыс жоғалды</h2>
<p>"Ештеңе өзгерген жоқ" — бұл шамамен 80% рас. Бір маңызды нәрсе шынымен өзгерді, әрі бұл ұсақ-түйек емес.</p>
<p>Бұрын орта үйренуге <strong>мәжбүрлейтін</strong>. Жұмыс істейтін кодты кемінде істеуге жарайтындай түсінбей тұрып іске қоса алмайтынсың. Компилятор, баг, құлау — бұлар әркімді күшпен базалық деңгейге тартатын <strong>ұстаздар</strong> еді. Күрес міндетті болатын — әрі ол онша тырыспайтындардың бойында да түсінік қалыптастыратын.</p>
<p>Енді бұл ұстаз <strong>өшірілген</strong>: қалай жұмыс істейтінін <strong>бірде-бір рет түсінбей-ақ</strong> жұмыс істейтін нәтиже ала аласың. Түсінбегені үшін сол сәттегі жаза жоғалды. Осыдан "ештеңе өзгерген жоқ" деген тезис жіберіп алатын салдар туады:</p>
<blockquote>Алшақтық тек бекіген типтердің арасында ғана тереңдеп жатқан жоқ — жаңа орта бұрын үйренетіндерді де сонда тартады, өйткені жалқау жол енді дереу жазасыз жұмыс істейтін нәтиже береді.</blockquote>
<p>Сондықтан "жалқау жалқау күйінде қалады" деген де тым тар. Бұл мінезден гөрі <strong>орта мен әдеттер</strong> туралы: адамдардың көбі "тумысынан" жалқау емес, үйкеліс пен кері байланысқа жауап береді. Үйкелісті алып таста — үйренуге бейімдер де сырғанайды. Тәуекел тек жалқауларда емес.</p>
<p>Ең нәзігі — <strong>біліктілік елесі</strong>. AI олқылықтарды соншалық тегіс жамайды, өз надандығыңның қабырғасына соғылмайсың да, оны байқамайсың да. "Бәрі жұмыс істеп тұр" болғандықтан түсінемін деп ойлайсың (<em>metacognitive laziness</em> әсері). Сондықтан <strong>тырысқақ болып қалу қиындай түсті</strong>: енді бұл өзіңе саналы түрде үйкеліс жасауды талап етеді — кейде AI-ды өшіру, онсыз түсіндіріп немесе жазып көру, "мен шынымен түсінемін бе, әлде түсінемін деп ойлаймын ба" дегенді тексеру. Бұрын мұны сенің орныңа шындық істейтін — енді бұл сенің жауапкершілігің.</p>
<blockquote>Ойын ережелері сол күйінде, бірақ ойынның өзі қулана түсті — жеңіл жол енді табысқа ұқсап бүркенеді.</blockquote>
<h2>Әлдеқашан білетіндер үшін — ойын басқаша</h2>
<p>Жоғарыдағының бәрі дағдыны нөлден <strong>қалыптастыру</strong> туралы. Егер сен әлдеқашан қалыптасқан болсаң — сен үшін сурет керісінше, әрі мұны ашық айту маңызды, әйтпесе пост "AI жаман" деп оқылады, ал бұл ондай тезис емес.</p>
<p>Тәжірибелі маман өз шегін біледі. AI не бергенін көреді → жарты секундта қай жері "олай емес", қай жері "иә, бірақ бұл жағдайға емес", қай жері "иә, әрі осы жерде, дәл қазір жарайды" екенін түсінеді. Онда нақты оқиғалардың жылдарынан құрастырылған "мұнда бірдеңе дұрыс емес" деген кірістірілген детектор бар. AI бұл детекторды өшірмейді — керісінше, оны тезірек қоректендіреді: көбірек мысал, көбірек жағдай, тез сүзіп тастайтын көбірек patterns.</p>
<p>Сондықтан жаңадан бастаушыны операторға айналдыру қаупі бар дәл сол AI сениорды турбо-сениорға айналдырады. 3 минуттың орнына 30 секундтық boilerplate. Жаңа кітапхананы зерттеу — бір аптада емес, бір кеште. Модульді Python-нан TypeScript-ке көшіру — "бір кездері кейін" емес, шынымен қолжетімді. Бұл — әлдеқашан жиналған қозғалтқышқа құйылған нитро.</p>
<p>Тағы бір нәзік нәрсе: <strong>AI тәжірибелі маманға байқатпай өтірік айта алмайды</strong>. AI оған жаңадан бастаушыға қаншалық жиі өтірік айтса, соншалық жиі айтады — тек ол мұны сөйлемді оқып бітпей тұрып ұстап алады. Жаңадан бастаушы үшін біліктілік елесінің тұзағы болатын нәрсе сениор үшін күнделікті ағындағы тағы бір баг-репорт қана.</p>
<blockquote>Уақытын әлдеқашан салғандар үшін AI — турбо. Әлі салмағандар үшін — салмаудың тағы бір себебі. Осы екі күйдің арасында орта жол жоқ — әрі бұл &quot;нақ қашан үйрендің&quot; дегенді мансаптағы ең маңызды факторлардың біріне айналдырады.</blockquote>
<h2>Жүйелік тәуекел: &quot;сынған саты&quot;</h2>
<p>Сениорлар джуниорлардан шығады. Егер AI джуниор жұмысын жеп қойса әрі ешкім іргетас қаламаса → 5–10 жылдан кейін <strong>сениор етіп өсіретін ешкім қалмайды</strong>. Мамандардың "жоғалған ұрпағы" тәуекелі шынайы — әрі бұл дүрліктіру емес, қарапайым piping-мәселе: жүйенің кіріс құбыры бітеліп, шығыс құбыры ашық қалды.</p>
<p>Неге бұл нөлге жетпейді (бірақ ауыр тиеді): "бәрі үйренуді тоқтатты" деген тепе-теңдік тұрақсыз. Тапшылық үйренгендерді аса құнды етеді → үйренуге деген ынта қайта оралады. Мамандарды саналы түрде өсіретін компаниялар moat алады. Бірақ өту ауыр: AI анық әрі сенімді өтірік айтқанда, ал айналада шындықты ажырататын ешкім болмағанда, операторлар бірінші елеулі оқиғада құлайды.</p>
<h2>Менің ұстанымым</h2>
<ul><li>Тілді үйрену <strong>міндетті</strong> — жаттауға емес, <em>оқу мен пайымға</em> бағыттап. Жүйрік оқуға апарар жолды қысқарту мүмкін емес; AI бұл жолды қысқартпайды, тек оны байқаусызда қосымша (опциялық) етеді.</li><li>Көпшілік "түсінуді" AI-ға беріп жатқанда — <strong>қазір үйрену — мүмкіндік терезесі</strong>: AI-мен бәсекелеспей, оны басқаратын тапшы маманға айналасың. Бұл — қысқа мерзімді терезе, нарық ерте ме, кеш пе бейімделеді. Ол ашық тұрғанда — пайдаланбау күнә.</li><li>Сау режим: үйренуден қашу үшін емес, жылдамырақ жүру <strong>әрі тереңірек үйрену</strong> үшін AI-ды пайдалану. Антипаттерн — "бүкіл тапсырманы менің орныма істеп бер". Паттерн — өзің жазасың, AI "неге" екенін түсіндіреді, ал сен оның нұсқасын қабылдамас бұрын өзің тереңірек түсінесің.</li><li>Жолға арналған парадокс: AI-ды тиімді пайдалану үшін жеткілікті білуің керек, <strong>қарапайым нәрседе ол қажет болмайтындай</strong>. Қарапайым нәрседе AI-ға тәуелді адам — күрделіде AI-дан алданады, әрі мұны байқауға мүмкіндігі болмайды.</li></ul>
<h2>Дереккөздер</h2>
<ul><li>Nature — <em>Is AI ruining our skills? Early results are in</em> (2026 маусым): <a href="https://www.nature.com/articles/d41586-026-01947-1">nature.com/articles/d41586-026-01947-1</a></li><li>InfoQ — Anthropic study, ~17% skill mastery drop: <a href="https://www.infoq.com/news/2026/02/ai-coding-skill-formation/">infoq.com/news/2026/02/ai-coding-skill-formation</a></li><li>TechCrunch — Coders are refusing to work without AI: <a href="https://techcrunch.com/2026/05/29/coders-are-refusing-to-work-without-ai-and-that-could-come-back-to-bite-them/">techcrunch.com/2026/05/29/coders-refusing-ai</a></li><li>AI &amp; Society — <em>AI deskilling is a structural problem</em>: <a href="https://link.springer.com/article/10.1007/s00146-025-02686-z">link.springer.com/article/10.1007/s00146-025-02686-z</a></li></ul>]]></content:encoded>
      <pubDate>Mon, 22 Jun 2026 00:00:00 GMT</pubDate>
      <category>ai</category>
      <category>learning</category>
      <category>career</category>
      <category>engineering</category>
    </item>
    <item>
      <title>Claude Code Multi-Profile, v2: таза XDG-архитектура</title>
      <link>https://oleksiimazurenko.dev/kk/blog/claude-profiles-clean-architecture</link>
      <guid isPermaLink="true">https://oleksiimazurenko.dev/kk/blog/claude-profiles-clean-architecture</guid>
      <description>Ескі alias негізіндегі тәсілде бір ай отырғаннан кейін, өзімнің Claude Code multi-profile баптауымды ~/.config/claude-profiles/ астындағы XDG-үйлесімді құрылымға қайта құрдым. Нағыз себебі — реттілік емес, ~/.claude.json-ның home-түбіріндегі бөлек файл екенін, онда --scope user арқылы қосылған MCP серверлері үнсіз тұрғанын ашу болды.</description>
      <content:encoded><![CDATA[<p>Бір ай бұрын мен <a href="/kk/blog/multiple-claude-accounts-one-device">бір машинада екі Claude Code аккаунтын қатар іске қосу туралы пост</a> жарияладым — жеке және жұмыс, <code>CLAUDE_CONFIG_DIR</code> мен shell alias арқылы. Тәсіл жұмыс істеді. Бәрі істеуге тиісін істеді.</p>
<p>Бірақ бір ай нақты қолданыстан кейін мен сол пост қамтымаған бірнеше елеулі мәселеге тап болдым. Ең үлкені — Claude Code-тың жасырын ерекшелігі, оған кездейсоқ тап болдым: Gmail MCP-ін қосқанымда, ол бес минуттан соң профилімнен "жоғалып кетті". Бүгін мен бәрін жаңа архитектураға қайта құрдым — XDG-үйлесімді, symlink пен <code>gum</code> арқылы интерактивті профиль таңдағышы бар. Бұл — v2, теориялық жақсарту емес, нақты тәжірибеден шыққан эволюция.</p>
<h2>Не мазалай бастады</h2>
<p>Төрт нәрсе. Алғашқы үшеуі — реттілік, көріну және масштаб туралы. Төртіншісі — бірден байқамаған нағыз архитектуралық тұзақ. Оларды ретімен қарастырамын, өйткені ақыр соңында дәл төртіншісі қайта құруға мәжбүрледі.</p>
<h3>1. $HOME-да екі бөлек қалта — бұл былық</h3>
<p><code>~/.claude</code> мен <code>~/.claude-promova</code> — $HOME түбірінде қатар тұрған екі dotfolder. XDG Base Directory Specification конфигтер <code>~/.config/</code> ішінде тұруға тиіс дейді. Тікелей home-да шашылып жатқан dotfile қалталары — уақыт өте $HOME-ды қоқыс тартпасына айналдыратын антипаттерн. Косметикалық, әрине, бірақ <code>ls -la ~</code> көрген сайын мазалайтын.</p>
<h3>2. Белсенді профильдің көрнекі растамасы жоқ</h3>
<p><code>claude</code>-ты іске қосамын да, қай профиль белсенді екенін білмеймін — сессия ішінде <code>claude config list</code> орындағанша. Қай терминалда нені іске қосқанымды ұмытсам, тексеруге тура келетін. Ұсақ нәрсе, бірақ personal + жұмыс екі қойындыда қатар жүргенде ол жиналып қалады.</p>
<h3>3. Alias-тар масштабталмайды</h3>
<p>Екі профиль — <code>claude</code> мен <code>claude-promova</code> — жарайды. Үшіншісін қосасың (фриланс клиенті) — үшінші alias керек. Төртіншісін — төртіншісі. Жарты жылдан соң қандай alias-тар жасағанымды есіме де түсіре алмас едім.</p>
<h3>4. ~/.claude.json ішіндегі жасырын тұзақ</h3>
<p>Ал міне, қайта құрудың нағыз себебі осы. Claude Code-та конфигурацияның <strong>екі түрлі орны</strong> бар, әрі құжаттама бұл туралы айқайламайды: <code>~/.claude/</code> — <code>projects/</code>, <code>sessions/</code>, <code>hooks/</code>, <code>skills/</code> жататын каталог. Әрі бөлек — <code>~/.claude.json</code>, тікелей $HOME түбіріндегі файл, онда <code>oauthAccount</code>, <code>mcpServers</code>, <code>projects</code> тарихы, <code>skillUsage</code> және нақты live күйдің тағы шамамен 40 өрісі тұрады.</p>
<p><code>claude mcp add --scope user</code> командасы дәл сол home-түбіріндегі <code>~/.claude.json</code>-ға жазады, <code>~/.claude/.claude.json</code>-ға да, профиль каталогына да <strong>емес</strong>. Мен мұны білмедім. Бір күні тап болғанша.</p>
<h2>Ашылу: Gmail MCP неге &quot;жоғалды&quot;</h2>
<p>Бүгін таңертең мен Claude Code-та Gmail MCP-ін баптап жатырмын. Әдеттегі баптау: Google Cloud жобасы, OAuth деректемелері, <code>claude mcp add gmail --scope user -- npx -y @gongrzhe/server-gmail-autoauth-mcp</code>. Бәрі жақсы. Сессияны қайта іске қостым — жұмыс істейді, поштаны оқып, хаттарға жауап беріп отырмын. Бір сағаттан соң біз alias-тарды <code>gum</code> профиль таңдағышы бар функцияға рефакторлай бастадық, содан кейін бәрін XDG-ге көшірдік. Мен <code>mv ~/.claude → ~/.config/claude-profiles/personal</code> жасадым, CC-ды қайта іске қостым, мәзірден personal-ды таңдадым. Ал жаңа сессияда <code>/mcp</code> аштым:</p>
<pre><code>figma            (failed)
playwright-test
claude.ai Notion</code></pre>
<p>gmail жоқ. vaultforge жоқ. Тек үш сервер, оның біреуі тіпті failed болған. Ал мен Gmail-ды жаңа ғана қосқам. Сол уақытта басқа терминалдағы сессия (жұмыс профилі) Gmail мен Vaultforge-ті еш мәселесіз көрсетіп тұрды.</p>
<p>Мен қаза бастадым да, машинамда <code>.claude.json</code> деп аталатын <strong>үш</strong> түрлі файл барын таптым:</p>
<table><thead><tr><th>Path</th><th>Size</th><th>mcpServers</th></tr></thead><tbody><tr><td>&lt;code&gt;~/.claude.json&lt;/code&gt; (home root)</td><td>113 KB</td><td>figma, gmail, vaultforge</td></tr><tr><td>&lt;code&gt;~/.config/claude-profiles/personal/.claude.json&lt;/code&gt;</td><td>29 KB</td><td>figma, playwright-test</td></tr><tr><td>&lt;code&gt;~/.config/claude-profiles/promova/.claude.json&lt;/code&gt;</td><td>40 KB</td><td>figma, playwright-test, vaultforge</td></tr></tbody></table>
<p>Міне, осы. Дәл осы — сол архитектуралық тұзақ:</p>
<ol><li><code>claude mcp add --scope user</code> <code>CLAUDE_CONFIG_DIR</code>-ге қарамастан әрдайым home-түбіріндегі <code>~/.claude.json</code>-ға жазады</li><li><code>CLAUDE_CONFIG_DIR</code> орнатылғанда Claude Code <code>$CLAUDE_CONFIG_DIR/.claude.json</code>-ды оқиды — профиль ішіндегі файлды</li><li>"user-scope MCP-тер" жазбалары мен "профиль MCP-тері" жазбалары бірдей атаулы <strong>бөлек файлдарда</strong> тұрады — әрі оларды шатастыру оңай</li></ol>
<p>Менің жағдайымда <code>~/.claude.json</code> (home-түбір, 113 KB) тірі, өзекті күй болды — gmail, vaultforge, OAuth сессиясы, бәрімен. Ал <code>~/.config/claude-profiles/personal/.claude.json</code> (29 KB) ескі snapshot болып шықты, ол бұрынырақтан ескі <code>~/.claude/</code>-та жатқан — бәлкім ескі CC нұсқасы сонда жазған, бәлкім плагин. Екі файлға да <code>jq -r 'keys[]'</code> қолданғанда home-түбірдегі нұсқада snapshot-та жоқ 41 бірегей кілт барын көрсетті.</p>
<p>Ал бұл 41 кілт — қоқыс емес. Бұл — Claude Code-тың нақты күйі:</p>
<ul><li><code>skillUsage</code> — skill-дерді қолдану статистикасы</li><li><code>githubRepoPaths</code> — жоба навигациясына арналған репозиторийлер кэші</li><li><code>cachedGrowthBookFeatures</code> + <code>cachedStatsigGates</code> — feature flag-тер (оларсыз CC әр іске қосуда жаңасын тартады)</li><li><code>hasShownOpus45Notice</code>, <code>hasShownOpus46Notice</code>, <code>hasShownS1MWelcomeV2</code> — UI флагтары (оларсыз модаль терезелер келесі іске қосуда қайта шығады)</li><li><code>lastPlanModeUse</code>, <code>feedbackSurveyState</code>, <code>installMethod</code> — onboarding және UX күйі</li></ul>
<p>Егер жай ғана <code>mv ~/.claude ~/.config/claude-profiles/personal</code> merge-сіз жасасаң — бұның бәрін жоғалтасың. Welcome модальдары қайта оралады, githubRepoPaths іздеуі қайта жүреді, әрбір сауалнама сұрауы қайта келеді. Мен мұны істей жаздадым.</p>
<h2>Жаңа архитектура</h2>
<p>Бәрі <code>~/.config/</code> ішіндегі бір ата-каталогтың астында тұрады, дәл XDG қалағандай. Әр профиль өзіне-өзі жеткілікті — өзінің <code>.claude.json</code>-ын қоса, толық күйі бар. <code>~/.claude</code> кері үйлесімділік үшін personal профильге symlink ретінде қалады.</p>
<pre><code>~/.config/claude-profiles/
├── personal/
│   ├── .claude.json          ← full live state + MCP list (113 KB)
│   ├── projects/, sessions/, hooks/, skills/, ...
│   └── CLAUDE.md
└── promova/
    ├── .claude.json          ← work state + work MCPs (41 KB)
    └── projects/, sessions/, agents/, skills/, ...

~/.claude  →  symlink to ~/.config/claude-profiles/personal/</code></pre>
<p>$HOME түбірінде ешқандай <code>.claude.json</code> жоқ. Әр профиль — бәрін ұстайтын бөлек оқшауланған каталог: <code>projects</code>/<code>sessions</code> қалталары да, MCP серверлері мен OAuth токендері бар сол файл да. Профильге бір ғана шындық көзі.</p>
<h3>Symlink неге дәл personal-ға бағытталған</h3>
<p><code>~/.claude/</code> жолын hardcode ететін бәрі — ескі скрипттер, плагиндер, Claude Code IDE кеңейтімдері, <code>claude-powerline.json</code> сияқты statusline конфигтері — өзгеріссіз жұмысын жалғастырады. Symlink personal профильге сілтейді. Егер кездейсоқ <code>command claude</code>-ты (wrapper функциясын айналып өтіп) іске қоссаң — default-жол іздеуі арқылы тағы да personal-ға түсесің. Personal бұрынғысындай "үнсіз default" болады, бірақ енді физикалық түрде XDG орнында тұрады.</p>
<h3>Іске қосудағы интерактивті таңдағыш — функция + gum</h3>
<p>Alias-тардың орнына — <code>~/.zshrc</code> ішіндегі <code>claude()</code> функциясы, ол <code>gum</code> (Charm-ның TUI көмекшісі) арқылы көрсеткі пернелермен басқарылатын мәзір көрсетеді. Функция <code>claude</code> шақыруын shell деңгейінде ұстап қалады, профиль таңдауға мүмкіндік береді және сәйкес <code>CLAUDE_CONFIG_DIR</code>-мен <code>command claude</code>-ты іске қосады. Мұнда <code>command</code> маңызды — ол wrapper функциясын айналып өтіп, нақты бинарьді шақырады.</p>
<pre><code># brew install gum

# Claude Code: profile picker on launch
claude() {
  local profile
  profile=$(gum choose \
    --header &quot;Claude profile:&quot; \
    --cursor &quot;▸ &quot; \
    --selected.foreground 212 \
    --cursor.foreground 212 \
    --header.foreground 244 \
    &quot;personal&quot; &quot;promova&quot;) || return
  case &quot;$profile&quot; in
    personal) CLAUDE_CONFIG_DIR=&quot;$HOME/.config/claude-profiles/personal&quot; command claude &quot;$@&quot; ;;
    promova)  CLAUDE_CONFIG_DIR=&quot;$HOME/.config/claude-profiles/promova&quot;  command claude &quot;$@&quot; ;;
  esac
}</code></pre>
<p>Іске қосқанда былай көрінеді:</p>
<pre><code>Claude profile:
▸ personal
  promova</code></pre>
<p>↑↓ — жылжу үшін, Enter — таңдау, Esc — бас тарту (сонда Claude жай ғана іске қосылмайды). Профиль әрдайым көрініп тұрады — ұмыту мүмкін емес.</p>
<h2>Нөлден бастап (миграциялайтын v1 жоқ)</h2>
<p>Егер сен <a href="/kk/blog/multiple-claude-accounts-one-device">ескі екі-қалта + alias хагін</a> қолданып жүрсең — төмендегі миграция скриптіне өт. Бұл бөлім — нөлден таза құрастыру: жаңа машина немесе қарапайым қолданыстан қалған әдепкі <code>~/.claude</code>. Алдымен пішінін шеш: бір аккаунт әрі бірнеше профиль (жобаларды және олардың MCP жиынтығын оқшаулау үшін — жиі кездесетін жағдай), әлде профильге шынымен бөлек аккаунттар (жеке vs жұмыс).</p>
<h3>1. Каталогтарды орналастыр</h3>
<pre><code># prerequisites
brew install gum jq

# 1. XDG parent for all profiles
mkdir -p ~/.config/claude-profiles

# 2. fold an existing default install in as your first profile (no-ops on a fresh machine)
if [ -d ~/.claude ] &amp;&amp; [ ! -L ~/.claude ]; then
  mv ~/.claude ~/.config/claude-profiles/personal
  [ -f ~/.claude.json ] &amp;&amp; mv ~/.claude.json ~/.config/claude-profiles/personal/.claude.json
fi

# 3. one directory = one profile
mkdir -p ~/.config/claude-profiles/personal ~/.config/claude-profiles/work

# 4. these files will hold OAuth tokens — lock them down
chmod 600 ~/.config/claude-profiles/*/.claude.json 2&gt;/dev/null</code></pre>
<p>Бір профильге бір каталог, жалғыз XDG түбір-каталогының астында. Егер сен Claude Code-ты бұрын қолданған болсаң, сниппет бұрыннан бар <code>~/.claude</code>-ты <code>personal</code> ретінде ішіне жинайды әрі онымен бірге home-түбіріндегі <code>~/.claude.json</code> live күйін де көшіреді; шынымен жаңа машинада бұл жолдар жай ғана ешнәрсе істемейді. Қалғаны — алғаш іске қосқанда өздігінен толатын бос каталогтар — ал <code>chmod 600</code> маңызды, өйткені бұл файлдарда ақыр соңында OAuth токендері жатады.</p>
<h3>2. Әр профильді аутентификацияла</h3>
<pre><code># launch each profile once and authenticate it
CLAUDE_CONFIG_DIR=~/.config/claude-profiles/work claude
#   → run /login in the session, sign in, done.
# Same account for every profile? One /login is enough — macOS keeps it in the
# shared Keychain, so the other profiles are already authenticated.</code></pre>
<p>Әр профильді өзінің <code>CLAUDE_CONFIG_DIR</code>-мен бір рет іске қосып, <code>/login</code> орында. Басты нюанс: егер барлық профиль <strong>бір</strong> аккаунтты пайдаланса, тек бір рет кіресің — macOS тіркелгі деректемесін Keychain-нің жалғыз ортақ жазбасында сақтайды, сондықтан басқа әр профиль автоматты түрде аутентификацияланады.</p>
<p><strong>Профильге бөлек аккаунттар</strong> — қиынырақ жол: Keychain-нің сол ортақ жазбасы бір ғана логинді ұстайды, сондықтан екінші аккаунтпен басқа профильге кіру біріншісін қайта жазып жіберуі мүмкін. Егер бұл сенің жағдайың болса — <a href="/kk/blog/multiple-claude-accounts-one-device">бір машинада екі Claude аккаунтын іске қосу</a> бөлімінің арнайы нұсқаулығын ұста әрі сенер алдында өзіңнің Claude Code нұсқаңда тіркелгі деректемелерінің оқшаулануын тексер.</p>
<h3>3. Symlink пен picker</h3>
<pre><code># personal becomes the default target of ~/.claude (back-compat)
ln -s ~/.config/claude-profiles/personal ~/.claude

# add the claude() gum picker from the architecture section to ~/.zshrc, then:
source ~/.zshrc
claude        # ← now shows the profile menu on every launch</code></pre>
<p>Осы жолды hardcode ететін бәрі жұмысын жалғастыруы үшін <code>~/.claude</code>-ты personal профильге бағытта, содан кейін жоғарыдағы архитектура бөліміндегі <code>claude()</code> <code>gum</code> picker-ін <code>~/.zshrc</code>-ге тастап қой. Қайта жүктегеннен кейін <code>claude</code> әр іске қосуда профиль мәзірін көрсетеді — миграцияланған баптаумен бірдей соңғы күй, тек нөлден құрастырылған.</p>
<h3>4. Әр профильдің MCP-ін сарала</h3>
<p>Соңында, әр профильге тек оған көрінуге тиіс құралдарды ғана бер: сол профильдің <code>settings.json</code>-ында <code>disableClaudeAiConnectors: true</code> қой да, оған не қажет болса, <code>claude mcp add</code> арқылы қайта қос — дәл жоғарыдағы конекторлар бөліміндегідей. Дәл осы он алмастырмалы профильді мақсатқа арнап құрылған он профильге айналдырады.</p>
<h2>Миграция скрипті</h2>
<p>Мұны оқып отырып, <a href="/kk/blog/multiple-claude-accounts-one-device">ескі схемадан көшкісі келетіндерге</a> арналған. Ең сыни қадам — екіншісі: ол home-түбіріндегі <code>~/.claude.json</code>-ды personal профильде бұрыннан бары бар нәрсемен merge жасайды, <code>mcpServers</code> тізімдерін біріктіреді. Бұл қадамсыз профиль MCP-терін де, бүкіл live күйін де жоғалтады.</p>
<pre><code># 1. Move existing dotfolders into a new XDG-style parent.
#    APFS mv is an inode rename — safe even if claude --resume is open in
#    another terminal, file descriptors stay alive on the same inode.
mkdir -p ~/.config/claude-profiles
mv ~/.claude          ~/.config/claude-profiles/personal
mv ~/.claude-promova  ~/.config/claude-profiles/promova   # rename as needed

# 2. CRITICAL: merge ~/.claude.json (the live state — likely 100+ KB) into
#    the personal profile, unioning mcpServers so no MCP is dropped.
jq -s &apos;.[0] * {mcpServers: (.[0].mcpServers + .[1].mcpServers)}&apos; \
  ~/.claude.json \
  ~/.config/claude-profiles/personal/.claude.json \
  &gt; /tmp/personal-merged.json
mv /tmp/personal-merged.json ~/.config/claude-profiles/personal/.claude.json

# 3. Fix permissions — jq+mv inherits umask (likely 644), but this file
#    holds OAuth tokens. Tighten to 600 immediately.
chmod 600 ~/.config/claude-profiles/personal/.claude.json

# 4. Back up the orphaned home-root file (delete once verified)
mv ~/.claude.json ~/.claude.json.migrated.bak

# 5. Symlink for backward compatibility (statusline configs, IDE plugins,
#    anything that hardcodes ~/.claude/ keeps working unchanged)
ln -s ~/.config/claude-profiles/personal ~/.claude</code></pre>
<p>Рұқсаттар туралы үшінші қадам өз алдына маңызды. <code>jq | mv</code> umask 644-пен (бәріне оқылатын) файл жасайды. Онда OAuth токендері бар. Merge-тен кейін бірден <code>chmod 600</code> жасау міндетті.</p>
<p>Миграциядан кейін — барлық белсенді Claude сессияларын жауып, қайта аш, shell-ды қайта жүкте (<code>source ~/.zshrc</code> немесе жаңа терминал), <code>claude</code>-ты іске қос, профиль таңда, <code>claude mcp list</code> арқылы бүкіл MCP орнында екенін тексер. Бәрі жақсы болса — <code>~/.claude.json.migrated.bak</code>-ты өшір. Бірдеңе дұрыс болмаса — қайтару тривиалды: <code>mv ~/.claude.json.migrated.bak ~/.claude.json</code> да symlink-ты алып таста.</p>
<h2>Бонус: профиль сайын MCP конекторлары (ұйымың оларды ортақтастырғанда)</h2>
<p>Жоғарыдағы архитектура оқшаулаудың екінші осін ашады — әрі дәл осы менің шын мәнінде ең көп қолданатыныма айналды. Егер сенің Claude аккаунтың бір ұйымға тиесілі болса, ұйым <strong>қашықтағы MCP конекторларының</strong> ортақ жиынтығын тарата алады (Figma, тапсырма трекері, метрика бэкенді, дерекқор шлюзі, CMS…). Claude Code ұйым қосқан <strong>әрбір</strong> конекторды <strong>әрбір</strong> сессияға автоматты түрде тартады, өйткені олар сен іске қосқан каталогқа емес, аккаунтқа байланған.</p>
<p>Екі-үш конектормен бұл жақсы. Он жоба мен ондаған ұйым конекторымен бұл жарамай қалады: әр сессия өзі мүлдем тиіспеуге тиіс құралдарды жүктейді. Бұл модельдің құрал тізіміндегі шу, бұл бос кеткен контекст, әрі — шын ауыртатын тұсы — сессияға басқа жобаға тиесілі жүйеге қол созуға мүмкіндік береді. B жобасымен жұмыс істеп жатқанда A жобасының конекторы тіпті болмауға тиіс.</p>
<h3>Авто-тартуды өшір де, содан соң сарала</h3>
<p>Тетік — жалғыз баптау, <code>disableClaudeAiConnectors</code> (Claude Code v2.1.182+). Ол кез келген баптау scope-ында тұрады — оның ішінде профильдің өзіндік <code>settings.json</code>-ында да — сондықтан ол <strong>әр <code>CLAUDE_CONFIG_DIR</code> сайын</strong>. Оны <code>true</code> қой да, сол профиль ұйым конекторларын автоматты тартуды толық тоқтатады:</p>
<pre><code>{
  &quot;disableClaudeAiConnectors&quot;: true
}</code></pre>
<p>Содан кейін тек сол профильге шын қажет конекторларды қайта қосасың. Айқын қосылған серверлерді флаг <strong>тиіспейді</strong>, сондықтан олар әдеттегідей жүктеледі — нәтижесі профиль сайын сараланған ішкі жиын:</p>
<pre><code># add back only what this profile uses — writes to the profile&apos;s own .claude.json
claude mcp add --scope user --transport http figma  https://&lt;your-figma-connector&gt;/mcp
claude mcp add --scope user --transport http linear https://&lt;your-linear-connector&gt;/mcp</code></pre>
<table><thead><tr><th>Профиль</th><th>Нені жүктейді</th></tr></thead><tbody><tr><td>&lt;code&gt;frontend&lt;/code&gt;</td><td>Figma, Linear, Sentry</td></tr><tr><td>&lt;code&gt;backend&lt;/code&gt;</td><td>Postgres, Grafana, Linear</td></tr><tr><td>&lt;code&gt;content&lt;/code&gt;</td><td>Notion, Figma, Linear</td></tr></tbody></table>
<p>Сол аккаунт, сол ұйым каталогы — бірақ әр профиль тек өз бөлігін көреді, оған қоса саналы түрде қайталайтын ортақ конекторларды (тапсырма трекері әдетте бәрінде тұруға лайық).</p>
<h3>Сенер алдында мұны тексердім</h3>
<p>Құжаттаманың сөзіне сеніп қалғым келмеді, сондықтан бір профильде таза A/B жүргіздім — сол аккаунт, сол OAuth токені, жалғыз айырмашылық — сол бір <code>settings.json</code>:</p>
<pre><code>profile WITHOUT the flag
  /mcp → Figma, Linear, Grafana, Postgres, Notion, Sentry, …   (entire org catalog)

same account, profile WITH  disableClaudeAiConnectors: true
  /mcp → (nothing auto-loaded)

…then  claude mcp add  Figma + Linear
  /mcp → Figma, Linear                                         (exactly the curated subset)</code></pre>
<p>Ол уәде еткенін тап-тура істейді. Оған арқа сүйемес бұрын білуге тұрарлық екі ескертпе:</p>
<ul><li><strong>OAuth конекторын қайта қосу бір интерактивті логинді талап етеді.</strong> URL-дегі кілт арқылы аутентификацияланатын конекторлар <code>claude mcp add</code> арқылы толықтай автоматты қайтады. OAuth артындағы конекторлар сол профильде оларды алғаш қолданғанда браузерде бір реттік аутентификация сұрайды.</li><li><strong><code>claude mcp list</code> ұйым конекторларын мүлдем көрсетпейді</strong> — тек жергілікті анықталған және айқын қосылған серверлерді. Автоматты тартылғандары тек тірі сессияда <code>/mcp</code> арқылы көрінеді. <code>mcp list</code> олардан "бос" көрінгенде абыржыма.</li></ul>
<h2>Не аласың</h2>
<ul><li>$HOME-дағы екі dotfolder орнына бір ата-каталог — XDG-үйлесімді</li><li>Symlink <code>~/.claude/</code>-ты hardcode ететін бәрімен үйлесімділікті сақтайды</li><li>Әр профиль өзіне-өзі жеткілікті — оның толық күйі мен MCP-тері өзінің <code>.claude.json</code>-ында тұрады</li><li>Профильге бір ғана шындық көзі — home-түбіріндегі профильдікінен үнсіз ажырап бара жатқан жетім конфиг енді жоқ</li><li>Сезімтал деректердің (oauthAccount, токендер) рұқсаттары кепілді түрде 600</li><li>Әр іске қосуда белсенді профильдің көрнекі растамасы — қай профиль белсенді екенін ұмыту мүмкін емес</li><li>Үшінші профиль қосу = функцияның <code>case</code>-іне бір жол қосу, жаңа alias клондап, оның атын жаттау емес</li></ul>
<h2>Кемшін тұстары</h2>
<p>Ашығын айтқым келеді. Бұл — тегін жақсарту емес; онымен бірге бірнеше ымыраға келу де келді, әрі оларды алдын ала білген жөн.</p>
<ul><li><strong><code>gum</code> — қосымша тәуелділік</strong> (<code>brew install gum</code>, ~13 MB). Егер оны орнатқың мүлдем келмесе — zsh-тегі <code>select</code>-ке немесе қарапайым <code>read</code>-ке қайтуға болады. Жұмыс істейді, бірақ сондай әдемі көрінбейді әрі көрсеткі пернелермен навигация жоқ.</li><li><strong>Әр іске қосуда бір Enter басу.</strong> <code>claude</code>-ты күніне ондаған рет іске қосатындарға бұл ашуландыруы мүмкін. Балама төменде (direnv).</li><li><strong><code>~/.claude → personal</code> symlink-і personal-ды default етеді.</strong> Жұмыс профилін default ету керек болса, symlink-ты қайта бағыттауға тура келеді (<code>ln -sf</code>). Қиын емес, бірақ бұл "ұмыт та кет, ешнәрсе бұзылмайды" емес.</li><li><strong>Symlink теориялық тұрғыда бұзылуы мүмкін</strong>, егер бір құрал <code>~/.claude.json</code>-ды temp+rename паттерні (write-file-atomic) арқылы атомарлы қайта жазса. Іс жүзінде Claude Code-тың өзі олай істемейді, бірақ үшінші тарап плагиндерін орнатсаң — тексер.</li><li><strong>Егер профильдерде түрлі жоспарлы түрлі Anthropic аккаунттарың болса</strong> — ауысқаннан кейін Claude Code OAuth күйін синхрондап жатқанда секундтан аз кідіріс болуы мүмкін. Менің қолданысымда байқалмайды, бірақ ол нөл емес.</li></ul>
<h2>Қарастырған баламаларым</h2>
<p><strong>direnv</strong> — әр жобаның түбіріндегі <code>.envrc</code> негізінде <code>CLAUDE_CONFIG_DIR</code>-ды автоматты орнатады. Нөл интерактив, нөл шерту. Кемшілігі: әр work root-қа <code>.envrc</code> қою керек, ал танылмаған қалтада <code>claude</code>-ты іске қоссаң — default профильді аласың (ол қалағаның болмауы мүмкін). Аз ғана work root-та тұратын әрі ешқашан ештеңе шерткісі келмейтіндер үшін direnv шынымен жақсырақ.</p>
<p><strong>Symlink негізіндегі ауыстыру</strong> (<code>~/.claude</code> symlink-ын қайта бағыттау арқылы бір ғана белсенді профиль) — оны да қарастырдым да, бірден бас тарттым. Түрлі профильді екі терминалды бір мезгілде ашық ұстай алмайсың — жаһандық "ағымдағы" біреу-ақ. Мен үшін бұл — deal-breaker.</p>
<h2>Қорытынды</h2>
<p>v2 — жай ғана v1 үстіндегі жақсырақ UX емес. Бұл — Claude Code-та жасырын архитектуралық ерекшелік бар екенін мойындау (<code>~/.claude.json</code> — home-түбіріндегі бөлек файл, оған <code>--scope user</code> командалары <code>CLAUDE_CONFIG_DIR</code>-ге қарамастан жазады), әрі профильдер арасында нағыз оқшаулау керек болса, оны ескеруге тура келеді. <a href="/kk/blog/multiple-claude-accounts-one-device">Бірінші тәсіл (<code>~/.claude</code> + <code>~/.claude-promova</code> + alias)</a> 80%-ға жұмыс істеді, бірақ қалған 20% профильдер арасындағы күйдің үнсіз ығысуы түрінде көрінді. Енді ол ескерілген. Егер жаңа бастап жатсаң — бірден v2-ден баста. Егер v1-де отырсаң — миграция скрипті жоғарыда, көшу бес минут алады да, ештеңе бұзбайды (дәл <code>jq</code> merge — сені күйден айырылудан құтқаратын қадам).</p>]]></content:encoded>
      <pubDate>Sat, 13 Jun 2026 00:00:00 GMT</pubDate>
      <category>claude-code</category>
      <category>productivity</category>
      <category>cli</category>
      <category>devtools</category>
    </item>
    <item>
      <title>Саған өтірік айтпайтын Claude Code агенттерін қалай жазу керек</title>
      <link>https://oleksiimazurenko.dev/kk/blog/writing-specialized-agents</link>
      <guid isPermaLink="true">https://oleksiimazurenko.dev/kk/blog/writing-specialized-agents</guid>
      <description>Сенімді Claude Code агент пайплайндарын құрудың екі ережесі: бір агентке — бір мамандану, әрі жауап сандық болуы тиіс жердің бәрінде промпттың орнына shell командалары.</description>
      <content:encoded><![CDATA[<p>Сен Claude Code-қа <em>осы дизайнды жүзеге асыруды және оның Figma макетіне сәйкес келетінін тексеруді</em> тапсырдың. Ол былай деп жауап берді: <em>Дайын. Барлық бөлімдер сәйкес келеді, аралықтар дұрыс, түстер орнында.</em> Бетті аштың. Аралықтардың жартысы қате болды. Hover күйі мүлде жоқ еді. Түймелер бір реңкке ауытқыған. Модель қасақана өтірік айтқан жоқ — ол сенің <em>тексерілді</em> дегенді естігің келетінін болжады да, дәл сол токендер тізбегін генерациялады. Тексеру қадамы болған жоқ. Ол болуы да мүмкін емес еді — тексеру ground truth-пен салыстыруды талап етеді, ал бір контексттегі жалғыз агент өз жауабынан шығып, өзін тексере алмайды.</p>
<p>Екі ереже менің галлюцинацияға толы жұмыс ағындарымды сенімді пайплайндарға айналдырды: <strong>бір агент — бір мамандану</strong> және <strong>shell командасы ретінде орындалатын кез келген нәрсе shell командасы ретінде орындалуы тиіс</strong>. Бұл теория емес. Бұл — менің Claude Code-пен күнде істейтін ісім, әрі шын мәнінде нәтиже беретін паттерндер осылар.</p>
<h2>Неге жалпы бейінді агенттер өтірік айтады</h2>
<p>LLM — келесі токенді болжайтын жүйе. Промпт екі рөлді сұрағанда — <em>X-ті құрастыр</em> және <em>X-ті тексер</em> — модель бірінші рөлді аяқтайды да, екіншісінің шығысы қалай көрінетінін оны шын орындамай болжайды. Өзін-өзі тексеру құрылымдық тұрғыдан әлсіз: сол контекст, сол модель, сол соқыр аймақтар. Тексерудегі <em>өтті</em> құрастырудағы <em>өтті</em> дегенмен корреляцияланады — олар бірге сәтсіздікке ұшырайды.</p>
<p>Модель өзінің өтірік айтып жатқанын білмейді. Оның көзқарасы бойынша <em>мен бәрін мұқият тексердім</em> дегені <em>мен кодты жаздым</em> дегеннің дәйекті жалғасы. <em>Сенімдісің бе?</em> деген промпттердің галлюцинацияларды ұстай алмайтын себебі де осы: модель екінші жүрісте де дәл сондай сенімді. Сенімділік дұрыстықпен емес — келесі сөйлемнің қаншалықты сенімді естілетінімен корреляцияланады.</p>
<p>Шешім — <em>жақсырақ промпт</em> емес. <em>Абай бол</em>, <em>қайта тексер</em>, <em>галлюцинация жасама</em> — бұл нұсқаулар ештеңе бермейді. Шешім құрылымдық: агентті физикалық тұрғыдан жалған сыңай таныта алмайтындай етіп мамандандыр және сандық жұмысты shell арқылы жүргіз, сонда жауап токен ықтималдығынан емес, нақты күйден келеді.</p>
<h2>1-ереже: бір агент — бір мамандану</h2>
<p>Жұмысты бөлек контексттері бар бөлек агенттерге бөл. Әр агентте жалғыз жауапкершілік пен тар құралдар жиыны бар. Бүкіл ағын шеңбер бойымен жүгіретін бір агенттің орнына эстафетаға айналады:</p>
<ul><li><strong>Builder агенті:</strong> spec-ті алады, кодты жазады. Оның бар жұмысы осы. Оның <code>Read</code>, <code>Edit</code>, <code>Write</code>, <code>Bash</code> құралдары бар.</li><li><strong>Reviewer агенті:</strong> spec-ті және diff-ті алады, қабылдау критерийлерін тексереді. Таза контекст. Кодтың <em>қалай</em> жазылғанын білмейді — тек не шыққанын ғана. Оның <code>Bash</code>, <code>Read</code>, <code>Grep</code>, <code>Glob</code> құралдары бар — жазу құралдары мүлде жоқ.</li><li><strong>Analytics агенті:</strong> сұрауларды құрастырып әрі орындау арқылы деректер туралы сұрақтарға жауап береді. Тек <code>Bash</code>. Нақты команданы орындамай жауапқа жете алмайды.</li><li><strong>Оркестратор:</strong> әр агентті кезекпен жіберетін және бір агенттен ешқашан екіншісінің жұмысын істеуді сұрамайтын негізгі сессия.</li></ul>
<p>Нақты мысал: UI жүзеге асыру плюс Figma макетіне қарсы визуалды тексеру. Builder компоненттерді жазады да, diff-ті commit жасайды. Одан кейін оркестратор reviewer-ді дизайн URL-імен, diff-пен және нақты қабылдау критерийлерімен шақырады. Reviewer Playwright-ты іске қосады, скриншоттар түсіреді, оларды эталонмен салыстырады және нақты скриншот жолдары мен пиксель айырмашылықтарымен <code>PASS</code> немесе <code>FAIL</code> қайтарады. Builder тексеру қадамына мүлде жақындамайды — тексерудің шынайы болатын себебі де нақ осы.</p>
<p>Анти-паттерн — бұл мега-агент: <em>осы UI-ды құрастыр да, оның макетке сәйкес келетініне көз жеткіз</em> дейтін жалғыз промпт. Кепілдік беремін — ол бәрі сәйкес келеді деп хабарлайды. Сәйкес келмейді. <em>Мен тексердім</em> деген баяндау — бұл жай ғана <em>мен құрастырдым</em> дегеннен кейінгі ең ықтимал токендер тізбегі.</p>
<h2>2-ереже: әрдайым промпттың орнына shell</h2>
<p>Сандық барлық нәрсені, нақты күйге қатысты барлық нәрсені, жауабы дұрыс көрінгенімен қате болуы мүмкін барлық нәрсені — <code>sh</code> арқылы жүргіз. Агенттің жұмысы — команданы құрастырып орындау, содан соң оның шығысын оқу. Ақиқат көзі агент емес. Shell шығысы.</p>
<ul><li><strong>Санау:</strong> <code>wc -l logs.txt</code> — шындық. Модельден шыққан <em>шамамен 47 лог жолы бар</em> дегені — галлюцинация.</li><li><strong>Analytics:</strong> <code>psql -c "SELECT count(*) FROM events WHERE created_at &gt; now() - interval '30 days'"</code>. <em>Көлемін бағала</em> емес.</li><li><strong>Тесттер:</strong> <code>pnpm test --reporter=json | jq '.numFailedTests'</code>. <em>Не сәтсіз болғанын қорытында</em> емес.</li><li><strong>Git күйі:</strong> <code>git rev-list --count main..HEAD</code>, <code>git diff --stat</code>. <em>Коммиттерді сана</em> немесе <em>өзгерістерді сипатта</em> емес.</li></ul>
<p>Мұны бойыңа сіңіргеннен кейін агент санды ойдан шығаруға дайындалған әрбір орынды байқай бастайсың. <em>Мұнда шамамен 200 жазба бар сияқты...</em> — жоқ. <code>SELECT count(*)</code> орында. <em>Тесттердің көбі өтеді...</em> — жоқ. Тест жиынтығын іске қос, JSON-ды талда. Модель команданы құрастыруда керемет. Ал команданың өзі болуда сенімсіз.</p>
<h2>Өзім тап болған сәтсіздік режимдері</h2>
<p>Бұлар — болжамды мысалдар емес. Паттернді өзгертпей тұрып, олардың әрқайсысы маған нақты уақытыма шығын әкелді:</p>
<ul><li><strong>Елес тексеру.</strong> Агент <em>мен 14 бөлімнің бәрін макетпен салыстырып тексердім</em> деді. Ол макетті ашпады. Скриншот түсірмеді. Тексеру — баяндаудағы галлюцинацияланған қадам болды.</li><li><strong>Сенімді айтылған қате сандар.</strong> Analytics деректерінен monthly active users сұрадым. Шамамен 3× қателескен сан алдым. Модель нақты сұрауды орындаудың орнына үлгі жолдардан интерполяция жасады.</li><li><strong>Ойдан шығарылған файл өзгерістері.</strong> Агент <em>мен <code>config/feature-flags.json</code> файлын жаңарттым</em> деді. Жаңартқан жоқ. Ол тек ниеттенген еді. <code>git diff</code> бос болды.</li><li><strong>Жалған тест жүгірулері.</strong> <em>Барлық тесттер өтеді.</em> Бірде-бір тест орындалмады. Агент test runner-ді ешқашан шақырмады — ол runner шығысының қалай көрінетінін болжады.</li></ul>
<p>Төртеуі де сол екі ережемен шешіледі: агентті бөл, shell-ге ысыр. Reviewer-де <code>Write</code> жоқ, сондықтан ол файлдарды жалған өңдей алмайды. Analytics агентінде тек <code>Bash</code> бар, сондықтан ол сұраудан келмеген санды қайтара алмайды. Құрылымдық мүмкін еместік ізгі ниеттерді әрдайым жеңеді.</p>
<h2>Мұны Claude Code-та қалай құрылымдау керек</h2>
<p>Claude Code <code>.claude/agents/*.md</code> ішінде анықталатын sub-agent-тарды қолдайды. Әр агент файлы атауды, сипаттаманы, рұқсат етілген құралдар жиынын және жүйелік промптты жариялайды. Оркестратор (сенің негізгі сессияң) оларды <code>Agent</code> құралы арқылы жібереді. Мен reviewer үшін қолданатын анықтаманың түрі мынадай — қысқа, тар және код жазуға физикалық тұрғыдан қабілетсіз:</p>
<pre><code>---
name: reviewer
description: Reviews a diff against acceptance criteria. Cannot edit code.
tools: Bash, Read, Grep, Glob
---

You are a strict code reviewer. You receive:
- A diff (already produced by the builder)
- Acceptance criteria

Your job:
1. Run the build, the tests, the linter — through Bash.
2. Read the changed files directly.
3. Compare the actual behavior to the criteria.
4. Report PASS or FAIL with concrete evidence (command output, file excerpts).

You must NOT:
- Trust the builder&apos;s summary
- Assume anything was verified just because it was claimed
- Mark something PASS without running the actual check</code></pre>
<p>Құралдар жиынына назар аудар: <code>Bash, Read, Grep, Glob</code>. <code>Write</code> жоқ, <code>Edit</code> жоқ, <code>Agent</code> жоқ. Reviewer командаларды орындай алады, файлдарды оқи алады, паттерндерді іздей алады — басқа ештеңе жоқ. Егер ол галлюцинацияланған diff-ті <em>тексерілген</em> деп өткізуге әрекеттенсе, оның құрал шақыруларының пішіні мұны айқын көрсетеді: нақты тексерулер болмаған. Құрал шақыруларын аудиттеп, нақ не тексерілгенін көре аласың.</p>
<p>Оркестрация паттерні: негізгі сессия Builder-ді шақырады → күтеді → нақты өзгерісті қармау үшін <code>git diff</code>-ті өзі орындайды → Reviewer-ді spec-пен және diff-пен шақырады → үкімін оқиды. Негізгі сессия бір агенттен ешқашан екеуін де істеуді сұрамайды. Құрал шектеулері промпт нұсқауларынан күштірек: <em>тексеруді жалғандама</em> — бұл тілек. <code>Write</code>-тың болмауы — бұл факт.</p>
<h2>Тастау керек анти-паттерндер</h2>
<p>Промпттерде көретін, ешқандай әсері жоқ — немесе одан да жаманы, жалған қауіпсіздік сезімін беретін нәрселер:</p>
<ul><li><strong><em>Абай бол және жұмысыңды қайта тексер.</em></strong> Ешқандай қосымша мінез-құлық тудырмайды. Модель мұқият жұмысқа ұқсайтын нәрсені бұрыннан-ақ шығарады.</li><li><strong><em>Шынымен тексергеніңе көз жеткіз.</em></strong> <em>Шынымен</em> деген сөз модель әрекет ете алатын семантика қоспайды. Ол <em>шынымен</em> тексердім деп мәлімдейді.</li><li><strong><em>Галлюцинация жасама.</em></strong> Prompt engineering мемі. Галлюцинация — модель өшіре алатын ауыстырғыш емес.</li><li><strong>Агентке <em>кіші</em> сандарда сену.</strong> Дәл кіші сандарда ол ең сенімді түрде өтірік айтады. Адалдықтың табалдырығы жоқ.</li><li><strong>Адалдықты <em>мәжбүрлеу</em> үшін промптқа қосымша ережелер қосу.</strong> Құрылымдық түзетулер (бөлу + shell) промпт өзгертулерін әрдайым жеңеді. Егер ережені орындату қажет болса, оны қазақ тілінде емес, құралдарға қолжетімділікте кодта.</li></ul>
<p>Егер галлюцинацияларды ұстау стратегияң неғұрлым екпінді тұжырымдама болса, сенде стратегия жоқ. Сенде тек үміт бар.</p>
<h2>Ойлау моделі</h2>
<p>Агент — әріптес емес. Ол функция: <code>prompt → tokens</code>. Функция кодты жазуда керемет, ал дұрыс нәрсе істеген-істемегенін іштей саралауда нашар. Оның өз жұмысы туралы мәлімдемелерін гипотеза ретінде қабылда. Diff, exit коды, скриншот, жолдар саны — міне, дәлелдер осылар. Жүрістің соңындағы қорытынды — бүкіл жүйедегі ең өтірікшіл бет.</p>
<p>Мамандану — баяндаудың ауытқуынан сақтандыруың. Shell — сенің жалғыз ақиқат көзің. Builder жазады. Reviewer тексереді. Bash шешеді.</p>
<h2>Қорытынды</h2>
<p>Бір нәрсені есте сақтасаң: бір агентке өз шығысын әрі өндіруге, әрі бағалауға жол берме; әрі бірде-бір агентке команданы орындамай сандық сұраққа жауап беруге жол берме. Қалғанының бәрі осы екі ереженің салдары. Құралдарға қолжетімділікті агрессивті түрде баптап, қорытындылардың орнына құрал шақыруларын аудитте — сонда галлюцинация беті <em>барлық жерден</em> <em>өзің қарауды бұрыннан білетін бірнеше нақты орынға</em> дейін тарылады.</p>]]></content:encoded>
      <pubDate>Fri, 29 May 2026 00:00:00 GMT</pubDate>
      <category>claude-code</category>
      <category>agents</category>
      <category>ai</category>
      <category>prompt-engineering</category>
      <category>automation</category>
    </item>
    <item>
      <title>Бір құрылғыда бірнеше Claude Code тіркелгісін іске қосу</title>
      <link>https://oleksiimazurenko.dev/kk/blog/multiple-claude-accounts-one-device</link>
      <guid isPermaLink="true">https://oleksiimazurenko.dev/kk/blog/multiple-claude-accounts-one-device</guid>
      <description>Екі (немесе одан көп) Claude Code тіркелгісін қатар — жеке және корпоративтік — бір ғана орта айнымалысы арқылы толық оқшаулаумен қалай қолдануға болады.</description>
      <content:encoded><![CDATA[<p>Мен Claude Code-ты күн сайын қолданамын — жеке жобаларға да, компаниядағы жұмысқа да. Мәселе мынада: бұл — OAuth сеанстары бөлек, CLAUDE.md нұсқаулары бөлек, MCP серверлері бөлек және жоба жады бөлек, мүлдем екі түрлі тіркелгі. Оларды бір құрылғыда бір жолдық shell alias арқылы қатар қалай іске қосатынымды осында көрсетемін.</p>
<h2>Мәселе</h2>
<p>Claude Code әдепкіде бәрін <code>~/.claude</code> ішінде сақтайды — OAuth токенін, әңгіме тарихын, глобалды CLAUDE.md-ты, жоба жадын, MCP серверлерінің конфигурацияларын және баптауларды. Екі тіркелгің болса, саған толықтай оқшауланған екі әлем қажет:</p>
<ul><li><strong>Жеке тіркелгі:</strong> өзіңнің Max/Pro жазылымың, өз қалауларың жазылған жеке CLAUDE.md, өз MCP серверлерің (Obsidian, жеке құралдар)</li><li><strong>Корпоративтік тіркелгі:</strong> компания басқаратын жоспар, Jira/Slack интеграциясының нұсқаулары бар жұмыстық CLAUDE.md, компанияның MCP серверлері</li><li><strong>Әртүрлі OAuth сеанстары:</strong> бір конфигурация каталогында бірден екі тіркелгіге кіріп отыру мүмкін емес</li><li><strong>Бөлек жоба жады:</strong> жұмыс жобаларының контексті жеке сеанстарға немесе керісінше сіңіп кеткенін қаламайсың</li></ul>
<p>Контекст ауысқан сайын шығып, қайта кіру — амал емес. Сеанс күйін жоғалтасың, әрі бұл жай ғана азапты.</p>
<h2>Шешім: CLAUDE_CONFIG_DIR</h2>
<p>Claude Code бір ғана орта айнымалысын мойындайды: <code>CLAUDE_CONFIG_DIR</code>. Оны кез келген жолға орнатсаң, Claude <code>~/.claude</code> орнына сол каталогты бәріне пайдаланады — аутентификация, тарих, баптаулар, жады, бәрі-бәрі. Бүкіл орнату 60 секунд алады.</p>
<h3>1-қадам: Екінші конфигурация каталогын жаса</h3>
<p>Өз қолданысыңа сай мағыналы атау таңда:</p>
<pre><code>mkdir ~/.claude-work</code></pre>
<p>Болды. Claude оны алғаш іске қосқанда қажетті құрылыммен толтырады.</p>
<h3>2-қадам: Екінші тіркелгіні аутентификациялап ал</h3>
<p>OAuth арқылы кіруді іске қосу үшін Claude-ты жаңа конфигурация каталогымен бір рет іске қос:</p>
<pre><code>CLAUDE_CONFIG_DIR=~/.claude-work claude</code></pre>
<p>Браузер ашылады. Корпоративтік тіркелгіңмен кір. OAuth токені <code>~/.claude-work</code> ішінде сақталады — <code>~/.claude</code> ішіндегі жеке сеансыңнан толықтай бөлек.</p>
<h3>3-қадам: Shell alias қос</h3>
<p>Айнымалыны есте сақтап жүрмеу үшін мынаны shell баптауыңа қос:</p>
<pre><code>alias claude-work=&apos;CLAUDE_CONFIG_DIR=~/.claude-work claude&apos;</code></pre>
<p>Shell-ды қайта жүкте:</p>
<pre><code>source ~/.zshrc</code></pre>
<h2>Не аласың</h2>
<p>Енді сенде толықтай оқшауланған екі Claude ортасы бар:</p>
<ul><li><strong><code>claude</code></strong> — жеке тіркелгіңмен, жеке CLAUDE.md-мен, жеке жадымен іске қосады</li><li><strong><code>claude-work</code></strong> — корпоративтік тіркелгіңмен, жұмыстық CLAUDE.md-мен, бөлек жадымен іске қосады</li><li><strong>Оқшауланған тарих:</strong> жұмыс әңгімелері жұмыста, жеке әңгімелер жеке ортада қалады</li><li><strong>Бөлек MCP серверлері:</strong> жеке Obsidian vault-ыңның MCP-і жұмыс сеанстарында көрінбейді</li><li><strong>Тәуелсіз баптаулар:</strong> әр тіркелгі үшін рұқсат етілген құралдар, рұқсат деңгейлері және модель қалаулары бөлек</li></ul>
<h2>Астыңғы қабатта қалай жұмыс істейді</h2>
<p>Конфигурация каталогы — Claude Code күйінің жалғыз шындық көзі. Әрқайсысының ішінде мынау жатыр:</p>
<pre><code>~/.claude/              ← personal account
├── CLAUDE.md           ← personal global instructions
├── projects/           ← personal project memory
├── settings.json       ← personal permissions &amp; MCP servers
└── ...                 ← OAuth session, history, cache

~/.claude-work/         ← corporate account
├── CLAUDE.md           ← company-specific instructions (Jira, Slack, etc.)
├── projects/           ← separate project memory
├── settings.json       ← different MCP servers, permissions
└── ...                 ← separate OAuth session</code></pre>
<p><code>claude-work</code> іске қосқанда, Claude бәрін <code>~/.claude-work</code> ішінен оқиды. Ол <code>~/.claude</code> бар екенін мүлдем білмейді. Екі дана толықтай тәуелсіз — тіпті оларды терминалдың бөлек қойындыларында бір мезгілде іске қоса аласың.</p>
<h2>N тіркелгіге дейін масштабтау</h2>
<p>Бұл үлгі кез келген тіркелгі санына кеңейеді. Бірнеше тапсырыс берушісі бар фрилансерсің бе? Тағы alias қос:</p>
<pre><code># Personal (default — no alias needed)
# Just run: claude

# Corporate
alias claude-work=&apos;CLAUDE_CONFIG_DIR=~/.claude-work claude&apos;

# Freelance client
alias claude-client=&apos;CLAUDE_CONFIG_DIR=~/.claude-client claude&apos;</code></pre>
<p>Әр alias өз конфигурация каталогын, өз OAuth сеансын, тапсырыс берушіге тән нұсқаулары бар өз CLAUDE.md-ын алады. А тапсырыс берушісінің кодтық базасы мен Jira жобасын білетін бір CLAUDE.md, ал Б тапсырыс берушісіне мүлдем басқа CLAUDE.md жасауыңа болады.</p>
<h2>Тәжірибелік кеңестер</h2>
<ul><li><strong>Каталогтарды анық атап қой:</strong> <code>~/.claude-work</code>, <code>~/.claude-clientname</code> — олардың саны үш-төртке жеткенде өзіңе рахмет айтасың</li><li><strong>Әрқайсысына арнайы CLAUDE.md жаз:</strong> жұмыстық CLAUDE.md компанияға тән нұсқауларды қамти алады (Jira тикеттерін қалай жасау, қай Slack арналарына сілтеу, деплой процедуралары). Жеке CLAUDE.md жеңіл әрі ықшам күйде қалады.</li><li><strong>Әр тіркелгіге бөлек MCP серверлері:</strong> жұмыстық құралдарды (Jira MCP, Slack MCP, ішкі API-лер) тек жұмыстық конфигурацияда бапта. Жеке конфигурацияңды таза ұста.</li><li><strong>Қай тіркелгі белсенді екенін тексер:</strong> қай тіркелгіде екеніңе сенімсіз болсаң, сеанс ішінде <code>claude config list</code> іске қос — ол конфигурация каталогының жолын көрсетеді</li></ul>
<h2>Бұл тәсіл қай жерде әлсіз болады</h2>
<p><code>CLAUDE_CONFIG_DIR</code> <em>тіркелгі</em> бойынша оқшаулайды, <em>жоба</em> бойынша емес. Бір профильдің ішінде Claude сол тіркелгі үшін бұрын тіркеген барлық MCP серверлерін көреді — барлық жобаларың бойынша. Жалғыз өзің жеке қолдансаң, әдетте бұл мәселе емес. Бірақ бір тіркелгінің астында бірнеше өндіріс үшін маңызды жоба пайда болғанда, әсіресе төлем, әкімшілік құралдар немесе инфрақұрылым сияқты қиылысатын салаларда, нақты жобааралық тәуекел туады: жасанды интеллект көмекшісі Б жобасымен жұмыс істеп жатқанда А жобасының құралын шақырып жіберуі мүмкін, әсіресе екеуі де атауы ұқсас операцияларды ұсынғанда.</p>
<p>Профиль үлгісі <em>мен қай тіркелгідемін?</em> деген сұраққа жауап береді. Ол <em>дәл қазір қай жобаның құралдары белсенді болуы керек?</em> деген сұраққа жауап бермейді. Ставкасы жоғары жұмыс үшін тіркелгілер бойынша бөлудің үстіне екінші оқшаулау қабатын қос:</p>
<ul><li><strong>Тек тіркелгіге ғана емес, әр өндіріс үшін маңызды жобаға жеке профиль:</strong> <code>~/.claude</code> мен <code>~/.claude-work</code> орнына <code>~/.claude-work-billing</code> және <code>~/.claude-work-admin</code> жаса. Әр профиль өзіне шынымен қажет MCP серверлерін ғана көреді.</li><li><strong><code>.mcp.json</code> арқылы жоба ауқымындағы MCP:</strong> жобаның түбіріне тек сол жобаның MCP серверлерін тізетін <code>.mcp.json</code> файлын коммит жаса. Claude сол каталогтан іске қосылғанда оларды өзі қосып алады. Ғаламдық конфигурацияңды барынша ықшам ұста — тек әмбебап құралдар (жазбалар, іздеу), ешқандай өндірістік endpoint жоқ.</li><li><strong>MCP серверлерін бір мәнді етіп ата:</strong> <code>admin</code>, <code>billing</code>, <code>mcp-server</code> секілді жалпылама атаулардан аулақ бол. Жоба атауын префикс ретінде қой: <code>acme_billing_prod</code>, <code>acme_admin_stage</code>. Сипаттамалы атау бірдеңе қате контекстен шақырылғалы тұрғанда сені тоқтап ойлануға мәжбүрлейді.</li><li><strong>Әр MCP құрал шақыруын мақұлдамас бұрын қарап шық:</strong> <code>*_create_*</code>, <code>*_delete_*</code>, <code>*_charge_*</code> тәрізді шақырулар саналы түрде екінші рет қарауға тұрады. Жаппай автомақұлдаудан ұтқан кез келген жылдамдығың қате жобаның құралы өндіріске бірінші рет атылған сәтте буланып жоғалады.</li></ul>
<p>Жалпы ереже: профильдерді батыл бөл, өндірістік деңгейдегі MCP-ті әдепкі профильден аулақ ұста және жобалар арасындағы құрал атауларының қиылысын рефакторингке тұратын жаман белгі деп есепте.</p>
<h2>Қорытынды</h2>
<p>Бір ғана орта айнымалысы. Бір ғана alias. Тіркелгілер арасында толық оқшаулау. Ешқандай logout/login биі жоқ, конфигурация қайшылықтары жоқ, контекст ағып кетуі жоқ. Бұл — тым қарапайым болғаны сонша, тіпті көңіліңді қалдыратындай шешім — бірақ дәл осы оны жақсы етеді. Бір рет бапта да, ол жайлы қайтып ешқашан ойлама.</p>]]></content:encoded>
      <pubDate>Tue, 05 May 2026 00:00:00 GMT</pubDate>
      <category>claude-code</category>
      <category>productivity</category>
      <category>cli</category>
      <category>devtools</category>
    </item>
    <item>
      <title>Claude Desktop-ты Obsidian-ға қалай жалғау керек — 4 MCP сервер арқылы саяхат</title>
      <link>https://oleksiimazurenko.dev/kk/blog/claude-obsidian-mcp-servers</link>
      <guid isPermaLink="true">https://oleksiimazurenko.dev/kk/blog/claude-obsidian-mcp-servers</guid>
      <description>Claude арқылы Obsidian қоймасының рефакторингін автоматтандырудың тұрақты жолын іздеудің нақты тарихы. Не бұзылды, не жұмыс істеді және неге VaultForge жалғыз жұмыс істейтін нұсқа болып шықты.</description>
      <content:encoded><![CDATA[<p>Елестетіп көріңіз: Obsidian-да жылдар бойы жинақталған 400-ден астам жазбаңыз бар. Бәрі қойма түбірінде шашылып жатыр, тұжырымдамалар техникалық жазбалармен араласқан, қайталанулар бар (<code>ideas.md</code> және ішінде 13 файл бар <code>ideas/</code> қалтасы), ешқандай жүйе жоқ. Тәртіп орнатқыңыз келеді — дұрыс қалталар архитектурасын құру, MOC файлдарын қосу, тегтерді реттеу. Мұны қолмен істеу — ұзақ әрі жалықтырады. Қисынды ой: <strong>Claude-ты MCP арқылы Obsidian-ға жалғап, рефакторингті AI жасасын</strong>. Шындығында бұл — мина алаңы арқылы өтетін жол болып шықты. Жұмыс істейтін шешімге жету үшін неден өткенімді айтайын.</p>
<h2>MCP дегеніміз не және неге бұл соншалықты оңай емес</h2>
<p>MCP (Model Context Protocol) — Claude-қа сыртқы құралдар мен деректерге жалғануға мүмкіндік беретін Anthropic-тің ашық протоколы. Принципі қарапайым: жергілікті сервер іске қосылады, ол "құралдарды" (tools) ұсынады, ал Claude оларды әңгіме барысында шақырады.</p>
<p>Obsidian үшін теориялық тұрғыда MCP серверлері жеткілікті. Тәжірибеде — әрқайсысының өз мәселелері бар.</p>
<p><strong>Obsidian экожүйесінің басты мәселесі:</strong> Obsidian — ресми MCP-сі жоқ жабық қосымша. Қауымдастық бұл олқылықтың орнын толтырды, бірақ әрбір іске асыру өз жолымен жүреді, әрі ешқайсысында "ресми бата" жоқ.</p>
<h2>1-әрекет: MarkusPfundstein/mcp-obsidian</h2>
<p>Іздеген кезде табылатын алғашқы құрал. GitHub-та 3400 жұлдыз, барлық оқулықтарда бар. Қауіпсіз таңдау сияқты көрінеді.</p>
<p><strong>Қалай жұмыс істейді:</strong> Obsidian-дағы Local REST API плагинінің негізіндегі Python сервері. Сервер плагинге HTTPS арқылы жүгінеді, ал плагин операцияларды Obsidian API арқылы орындайды.</p>
<h3>Не дұрыс болмады</h3>
<ul><li>17 ай бойы жаңартылмаған</li><li>85 ашық issue</li><li><strong><code>move</code>/<code>rename</code> жоқ</strong> — тек read, write, append, delete</li><li>Local REST API-де құжатталған деректер жоғалту багы бар: POST endpoint append кезінде файлды үнсіз қайта жазып жіберуі мүмкін</li></ul>
<p>Рефакторингке жарамайды — бізге файлдарды жылжытып, сілтемелерді сақтау қажет. Әрі қарай жүреміз.</p>
<h2>2-әрекет: aaronsb/obsidian-mcp-plugin</h2>
<p><strong>Obsidian-ның нативті плагині</strong> ретінде жұмыс істейтін нұсқаны таптым. Бұл Obsidian-ның ішкі API-іне тікелей қол жеткізуді білдіреді — backlinks, Dataview, байланыстар графы. Нативті API арқылы move барлық вики-сілтемелерді автоматты түрде жаңартады, өйткені Obsidian мұны өзі өңдейді.</p>
<h3>Орнату қиындықтары</h3>
<ul><li>Плагин <strong>Obsidian-ның ресми каталогында жоқ</strong> (PR валидация қателерімен ілініп тұр)</li><li><strong>BRAT</strong> (Beta Reviewers Auto-update Tool) арқылы орнату керек</li><li>Claude Desktop Bearer token-ды UI арқылы тікелей қабылдамайды — плагинде HTTPS-ті қосуға мәжбүр етті</li><li>localhost үшін self-signed сертификат сенім мәселелерін тудырады</li></ul>
<p>Осы айналма жолдардың бәрі арқылы ақыры жалғадым. Негізгі сынақ — <code>vault.move</code> шынымен <code>[[wikilinks]]</code> қайта жазады, керегінше жұмыс істейді.</p>
<h3>Өндірісте не дұрыс болмады</h3>
<p>Жаппай рефакторингті бастаған кезде (Obsidian-да ондаған қалталарды drag-and-drop + бір мезгілде MCP операциялары), сервер <strong>4+ минутқа қатып қалды</strong>. Себебі: плагин Obsidian-ның <em>ішінде</em> жұмыс істейді. Obsidian құрылымның жаппай өзгеруінен кейін мыңдаған файлды қайта индекстегенде, плагин де онымен бірге бұғатталады.</p>
<p>Қорытынды: <strong>ашық Obsidian данасына және оның индексіне тәуелділік жаппай операциялар үшін апатты</strong>.</p>
<h2>3-әрекет: @bitbonsai/mcpvault</h2>
<p>Қисынды тұрғыда — бізге <strong>Obsidian-ға тәуелді емес</strong> сервер керек. Дискідегі файлдармен тікелей жұмыс істейді. <code>@bitbonsai/mcpvault</code> — көптеген шолуларда ұсынылады. Файлдық жүйеге тікелей қол жеткізу, қарапайым баптау (<code>npx @bitbonsai/mcpvault@latest /path/to/vault</code>), 14 құрал. Obsidian-ды ашық ұстаудың тіпті қажеті жоқ.</p>
<p><strong>Орнатпас бұрын бір маңызды жайтты тексердім</strong> — move кезінде вики-сілтемелер жаңарады ма. Пайдаланушының пікірін таптым:</p>
<blockquote>Файлдық жүйе коннекторы өзінің Obsidian-да екенін білмейді — ол &lt;code&gt;.md&lt;/code&gt; файлдары бар қалтаны көреді, болды. Файл аттарының семантикалық салмақ көтеретінін, әрбір &lt;code&gt;[[wikilink]]&lt;/code&gt; қайта атау немесе жылжыту сәтінде бұзылатынын білмейді. Auto-update links тек қайта атау қосымшаның ішінен жасалғанда ғана іске қосылады. Мұны Claude-тан файл аттарын тазалауды сұрағаннан кейін, сілтемелерінің жартысы бұзылған dashboard-қа оралғанда білдім.</blockquote>
<p>mcpvault-тың өз құжаттамасында расталды: PR #101 (wiki link resolution) <strong>шолуда, merge жасалмаған</strong>. Яғни <code>mcpvault</code> арқылы move vault-тың жартысын бұзады. Жарамайды.</p>
<h2>4-әрекет: VaultForge (финал)</h2>
<p><code>blacksmithers/vaultforge</code> — рефакторинг жасайтын AI агенттеріне арнайы жасалған.</p>
<h3>Архитектуралық тұрғыда дұрыс</h3>
<ul><li><strong>Тікелей файлдық жүйе</strong> — Obsidian-ға тәуелді емес</li><li><strong>Өз wikilink қозғалтқышы</strong> — барлық түрлерін (stem, толық жол, alias, embed) жаңартатын <code>[[wikilink]]</code> шешу логикасын іске асырады</li><li><strong>Әдепкіде dry run</strong> барлық қиратушы операцияларда — алдымен нені өзгертетінін көрсетеді, содан кейін сіз растайсыз</li><li><strong>27 құрал</strong> бәсекелестердегі 8–14-ке қарсы: batch_rename, update_links, backlinks (әсерді талдау), prune_empty_dirs, frontmatter, smart_search (BM25), vault_themes (TF-IDF clustering)</li><li><strong>MIT лицензиясы</strong>, TypeScript, нөл ішкі тәуелділік</li><li><strong>30 секундта орнату</strong> <code>.mcpb</code> арқылы (Claude Desktop үшін one-click extension)</li></ul>
<h3>Оқшауланған файлдарда қауіпсіздік сынағы</h3>
<p>Айқас сілтемелері бар 4 сынақ файлын жасадым — stem сілтемелер, alias-пен сілтемелер, толық жолмен сілтемелер. Бір файлды ішкі қалтаға жылжытамын:</p>
<pre><code>delta.md → subfolder/delta-renamed.md</code></pre>
<p>VaultForge dry run көрсетті: "1 файл қайта аталады, 3 сілтеме жаңартылады". Шын мәнінде орындадым.</p>
<table><thead><tr><th>Link type</th><th>Before</th><th>After</th></tr></thead><tbody><tr><td>Stem</td><td>[[delta]]</td><td>[[delta-renamed]]</td></tr><tr><td>Alias</td><td>[[delta|D]]</td><td>[[delta-renamed|D]]</td></tr><tr><td>Full path + alias</td><td>[[_vf-test/delta|D]]</td><td>[[_vf-test/subfolder/delta-renamed|D]]</td></tr></tbody></table>
<p>Кейін тексердім — <strong>сілтемелердің үш түрі де дұрыс жаңарды</strong>. Дәл осы нәрсе бұрынғы барлық құралдарда жетіспеді.</p>
<h2>VaultForge-ті қалай орнату керек — түпкілікті нұсқаулық</h2>
<p>Егер сізде macOS және Claude Desktop болса:</p>
<h3>1-қадам</h3>
<p><code>.mcpb</code> файлын жүктеп алыңыз:</p>
<pre><code>curl -fsSL https://github.com/blacksmithers/vaultforge/releases/latest/download/vaultforge.mcpb \
  -o /tmp/vaultforge.mcpb &amp;&amp; open /tmp/vaultforge.mcpb</code></pre>
<h3>2-қадам</h3>
<p>Claude Desktop кеңейтімді орнату диалогын ашады. Vault-қа <strong>абсолютті жолды</strong> енгізіңіз — backslash-сыз, қалыпты бос орындармен:</p>
<pre><code>/Users/yourname/Library/Mobile Documents/iCloud~md~obsidian/Documents/MyVault</code></pre>
<h3>3-қадам</h3>
<p>Save түймесін басыңыз. Claude Desktop кеңейтімді конфигке автоматты түрде қосады. <strong>Қайта іске қосу қажет емес</strong> — <code>.mcpb</code> кеңейтімдері автоматты түрде қабылданады.</p>
<h3>4-қадам</h3>
<p>Тексеріңіз: жаңа чатта сұраңыз: <em>"What is the status of my Obsidian vault?"</em> — <code>totalFiles: 416, totalDirs: 135, ...</code> сияқты бірдеңе қайтуы керек</p>
<h2>Obsidian-ның MCP экожүйесі туралы не түсіндім</h2>
<p><strong>Біріншіден, "ең танымал" деген "жұмыс істейді" дегенді білдірмейді.</strong> MarkusPfundstein/mcp-obsidian-да 3400 жұлдыз бар және ол әдепкі ұсыныс, бірақ ол ескірген әрі негізгі операциялары жоқ.</p>
<p><strong>Екіншіден, нативті плагиннің жасырын құны бар.</strong> Aaronsb плагині мінсіз көрінді — graph, Dataview, нативті move. Бірақ жұмыс істеп тұрған Obsidian данасына және оның индексіне тәуелділік оны байыпты жаппай операцияларға жарамсыз етеді.</p>
<p><strong>Үшіншіден, link-engine-сіз тікелей файлдық жүйе — тұзақ.</strong> Mcpvault жылдам әрі қарапайым, бірақ "жай ғана файлдарды жылжыту" vault құрылымын бұзады. Сілтемелер файлдық жүйе білмейтін <strong>таңылған семантиканы</strong> көтереді. Өз wikilink логикасын іске асырмайынша, құрал минаға айналады.</p>
<p><strong>Төртіншіден, оқшауланған деректерде сынаңыз.</strong> Қандай да бір құралға жаппай рефакторингті сеніп тапсырмас бұрын — айқас сілтемелері бар 4–5 файлдан тұратын сынақ қалтасын жасап, не болатынын қараңыз. 5 минуттық сынақ backup-тан қалпына келтірудің сағаттарын үнемдейді.</p>
<p><strong>Бесіншіден, vault-тың git-бэкапын ұстаңыз.</strong> Бәрінің ішіндегі ең маңыздысы. Vault ішінде бір ғана <code>git init</code> және мерзімді коммиттер — бұл AI агентінің немесе құралдың кез келген қатесінен сақтандыру. Егер бірдеңе бұзылса — <code>git reset --hard</code> бәрін қайтарады.</p>
<h2>Қорытынды</h2>
<p>Жол бірнеше сағат пен үш сәтсіз әрекетке созылды. Түпкілікті архитектура былай көрінеді:</p>
<ul><li><strong>VaultForge</strong> — негізгі жұмыс құралы. Тікелей файлдық жүйе + өз wikilink қозғалтқышы + 27 құрал = кез келген ауқымдағы тұрақты рефакторинг.</li><li><strong>Git</strong> — vault-ты нұсқалау. Кез келген қате үшін тегін қайтару.</li></ul>
<p>Енді осының бәрі неден басталса, соны істей аламын: Claude-тан 400 жазбаны дұрыс PARA архитектурасына реттеуді, қайталануларды біріктіруді, frontmatter қосуды, MOC карталарын құруды сұрай аламын. Әрбір операция қауіпсіз, сілтемелер сақталады, dry run бірдеңе өзгермес бұрын не болатынын көрсетеді.</p>
<p>Егер сіз де өзіңіздің ретсіз Obsidian-ыңызға қарап, AI көмекшісін қаласаңыз — бірден VaultForge-тен бастаңыз. Менің өлі жобалар, beta плагиндер және link-логикасы жоқ файлдық жүйе серверлері арқылы өткен жолымды қайталамаңыз.</p>]]></content:encoded>
      <pubDate>Sun, 03 May 2026 00:00:00 GMT</pubDate>
      <category>Obsidian</category>
      <category>Claude</category>
      <category>MCP</category>
      <category>Automation</category>
      <category>AI</category>
    </item>
    <item>
      <title>Қара құрдымдар рекурсивті әлемдер ретінде: физикадан тіршілік мәніне дейін</title>
      <link>https://oleksiimazurenko.dev/kk/blog/black-holes-recursive-universes</link>
      <guid isPermaLink="true">https://oleksiimazurenko.dev/kk/blog/black-holes-recursive-universes</guid>
      <description>Егер әрбір қара құрдым жаңа әлемнің Үлкен Жарылысы болса ше? Рекурсивті космология, Хокинг сәулеленуі және танымдық жабықтық туралы зерттеу.</description>
      <content:encoded><![CDATA[<h2>TL;DR</h2>
<p>Егер әрбір қара құрдым жаңа әлемнің Үлкен Жарылысы болса ше? Бұл мақала біздің әлеміміз шексіз рекурсивті ағаштың бір түйіні болуы мүмкін деген идеяны зерттейді — мұнда қара құрдымдар суб-әлемдерді дүниеге әкеледі, энергия Хокинг сәулеленуі арқылы қайта оралады, ал физиканың іргелі заңдары әлемдер арасындағы байланысты мүмкін емес ету үшін әдейі жасалған.</p>
<h2>Қара құрдым = әлем</h2>
<p>Идея ой толғау сәтінде туды: жеткілікті масса мен қысым бір нүктеге шоғырланғанда қара құрдым пайда болады. Бұл сингулярлық — шексіз тығыздық, шексіз қисықтық — Үлкен Жарылысты сипаттайтын жағдайларға күдіктенерлік дәрежеде ұқсас.</p>
<p>Егер бұл әртүрлі жақтан қаралған бір оқиға болса ше? Сырттан біз материяны жұтып жатқан қара құрдымды көреміз. Іштен қарасақ — өмірге жарылып шыққан жаңа әлемді. Қара құрдымға құлаған масса мен энергия өзінің жұлдыздары, планеталары және, бәлкім, өз қара құрдымдары бар мүлдем жаңа ғаламның шикізатына айналады.</p>
<blockquote>Біздің әлемдегі әрбір қара құрдым бір әлемді қамтуы мүмкін. Ал біздің әлеміміз аталық әлемнің қара құрдымының ішінде өмір сүруі мүмкін.</blockquote>
<h2>Неліктен әлемдер бір-бірімен байланыса алмайды</h2>
<p>Міне, ең әсем тұсы: оқиғалар көкжиегінен өткеннен кейін кері қайту жоқ. Жалпы салыстырмалылық теориясы мұны кепілдейді — аталық әлемнің болашағы толығымен оқиғалар көкжиегінен тыс жатыр әрі іштен қол жеткізу мүмкін емес. Суб-әлемнің тұрғысынан алғанда, аталық әлем әлдеқашан аяқталған. Оның бүкіл уақыт сызығы өтіп кеткен.</p>
<p>Бұл — жетілдірілген технологиямен еңсеретін техникалық шектеу емес. Ол кеңістік-уақыттың геометриясының өзіне сіңірілген. Әлемдер бір-бірінен түбегейлі оқшауланған — қашықтықпен емес, уақыт құрылымымен.</p>
<h2>Энергия айналымы: қарызға алу мен қайтару</h2>
<p>Бірақ энергия жоғалмайды. Хокинг сәулеленуі — қара құрдымдардың баяу буланатын кванттық процесі — таңғажайып айналым жасайды:</p>
<ol><li>Аталық әлем қара құрдым жасап, энергияны суб-әлемге береді</li><li>Суб-әлем өзінің бүкіл өмірлік циклін триллиондаған жыл бойы өткізеді</li><li>Қара құрдым баяу буланып, Хокинг сәулеленуі арқылы энергияны аталық әлемге қайтарады</li><li>Аталық әлем өз энергиясын қайтарып алады — үстемесімен қоса</li></ol>
<p>Бұл «үстеме» қызық: физиктер қазір Хокинг сәулеленуі ақпаратты сақтайды деп есептейді. Аталық әлем жай ғана бос энергияны қайтарып алмайды — ол ішінде болған барлық нәрсенің ізін алады. Қалыптасқан әрбір жұлдыз, әрбір планета, сананың әрбір сәті — сәулеленуде кодталған.</p>
<h2>Түбіне дейін рекурсия</h2>
<p>Егер сіз бағдарламашы болсаңыз, бұл үлгіні бірден танисыз. Бұл — рекурсия. Әрбір әлем аз энергиямен <code>universe()</code> шақырып, суб-әлемдер жасайды, олар өз кезегінде суб-суб-әлемдер жасайды — қара құрдым түзуге энергия жетпей қалғанша, яғни базалық жағдайға дейін.</p>
<pre><code>universe(energy)
  ├── creates black holes
  │     ├── universe(energy - n)
  │     │     ├── universe(energy - n - m)
  │     │     │     └── base case: not enough energy for black holes
  │     │     └── returns energy via Hawking radiation
  │     └── returns energy via Hawking radiation
  └── receives all energy back</code></pre>
<p>Физик Ли Смолин осыған ұқсас идеяны <strong>Космологиялық Табиғи Сұрыпталу</strong> ретінде тұжырымдады: әлемдер қара құрдымдар арқылы «көбейеді», әрі әрбір ұрпақтың физикалық тұрақтылары сәл өзгеше болады — сансыз циклдар барысында көбірек қара құрдым, көбірек әлем тудыруға оңтайландырылған.</p>
<h2>Осы циклде біз қайдамыз?</h2>
<p>Біздің әлемге шамамен 13,8 миллиард жыл. Бұл көне болып естіледі, бірақ оның толық өмір ұзақтығы аясында біз небәрі ең басын көріп отырмыз:</p>
<table><thead><tr><th>Оқиға</th><th>Уақыт ауқымы</th></tr></thead><tbody><tr><td>Әлемнің қазіргі жасы</td><td>~10¹⁰ years</td></tr><tr><td>Жұлдыздар түзілуін тоқтатады</td><td>~10¹⁴ years</td></tr><tr><td>Қара құрдымдар дәуірі</td><td>~10⁴⁰ years</td></tr><tr><td>Соңғы қара құрдым буланады</td><td>~10¹⁰⁰ years</td></tr></tbody></table>
<p>Біз әлеміміздің жалпы өмір ұзақтығының шамамен 0,00000000...01% тұсында өмір сүреміз. Жұлдыздар дәуірі — біз көре алатын барлық нәрсе — ең басындағы қысқа жарқыл ғана. Әлемнің шынайы тарихы — қара құрдымдардың суб-әлемдерді жасап, буландыратын баяу да шыдамды дәуірі.</p>
<h2>Жоғары өлшемдер туралы мәселе</h2>
<p>Осыған дейін талқыланғанның бәрі біздің үшөлшемді түсінігіміз аясында әрекет етеді. Бірақ біздің әлеміміз жоғары өлшемді бір нәрсенің «тілімі» болса, онда қара құрдымдар мен суб-әлемдердің бүкіл рекурсивті ағашы біз қабылдай алмайтын құрылымның көлеңкесі ғана болуы мүмкін.</p>
<p>1884 жылы Эдвин Абботт <em>Флатландия</em> шығармасын жазды — үшінші өлшемді елестете алмайтын екіөлшемді тіршілік иелері туралы әңгіме. Флатландия арқылы өтетін шар үлкейіп, кішірейетін шеңбер түрінде көрінеді. «Флатландықтар» оны математикалық түрде сипаттай алады, бірақ көріп отырғанын ешқашан шынайы түсінбейді. Біз де өз әлемімізге қатысты дәл осындай жағдайда болуымыз мүмкін.</p>
<blockquote>Сана дегеніміз не? Субъективті тәжірибе неге бар? Дэвид Чалмерс мұны «қиын мәселе» деп атады — және бұл біздің өлшемдік ауқымымыздан тыс бірдеңенің әрекет ететінінің ең күшті дәлелі болуы мүмкін.</blockquote>
<h2>Бәрі де іргелі деңгейде құлыпталған</h2>
<p>Ең таңғаларлық түйсіну — біздің білмейтінімізде емес, керісінше біздің <em>біле алмайтынымызда</em>. Ізденістің әрбір бағыты іргелі кедергіге тіреледі:</p>
<ul><li><strong>Аталық әлемді көргіңіз келе ме?</strong> Оқиғалар көкжиегімен бөгелген</li><li><strong>Сананы түсінгіңіз келе ме?</strong> Бөгелген — жүйе өзін толық талдай алмайды (Гёдельдің толымсыздық теоремалары)</li><li><strong>«Бұрын» не болғанын білгіңіз келе ме?</strong> Бөгелген — уақыт Үлкен Жарылыстан басталды</li><li><strong>Жоғары өлшемдерді қабылдағыңыз келе ме?</strong> Үшөлшемді тіршілік иесінің танымдық шектеулерімен бөгелген</li></ul>
<p>Философ Колин Макгинн мұны <strong>танымдық жабықтық</strong> деп атайды: кейбір сұрақтар адам санасы үшін деректердің жеткіліксіздігінен емес, сананың өзінің құрылымынан жабық. «Біз әлі білмейміз» бен «біз біле алмаймыз» арасындағы айырмашылық орасан зор.</p>
<h2>Қалған жалғыз нәрсе: өзін-өзі жетілдіру</h2>
<p>Егер әрбір шығу жолы әдейі бөгелген болса — сыртқа қарай алмасаңыз, артқа қарай алмасаңыз, жоғары қарай алмасаңыз — онда бір ғана бағыт қалады: ішке қарай. Әлем адамды өзіне назар аударуға мәжбүрлеу үшін әдейі құрылған сияқты.</p>
<p>Бұл тұжырым діннен де, философия оқулықтарынан да шыққан жоқ. Ол қара құрдымдардың, рекурсияның, ақпарат теориясының және таным шектерінің логикасын қадағалаудан туындады. Экзистенциалистер, буддистер, стоиктер мен физиктер — бәрі әртүрлі жолмен бір нүктеге келеді: тіршіліктің мәні жай ғана өмір сүріп жатқан тіршілік иесін жетілдіруде болуы мүмкін.</p>
<blockquote>Біз бұған сеніммен емес, физикамен келдік — қара құрдымдардан бастап, рекурсивті әлемдер арқылы, білімнің іргелі бөгеттеріне дейін, жалғыз ашық есікке дейін: жақсырақ болу.</blockquote>
<h2>Дереккөздер</h2>
<ul><li><a href="https://en.wikipedia.org/wiki/Cosmological_natural_selection" target="_blank" rel="noopener">Ли Смолин — Космологиялық Табиғи Сұрыпталу</a></li><li><a href="https://en.wikipedia.org/wiki/Hawking_radiation" target="_blank" rel="noopener">Стивен Хокинг — Хокинг сәулеленуі</a></li><li><a href="https://en.wikipedia.org/wiki/Hard_problem_of_consciousness" target="_blank" rel="noopener">Дэвид Чалмерс — Сананың Қиын Мәселесі</a></li><li><a href="https://en.wikipedia.org/wiki/Flatland" target="_blank" rel="noopener">Эдвин Абботт — Флатландия: Көп өлшемді роман (1884)</a></li><li><a href="https://en.wikipedia.org/wiki/G%C3%B6del%27s_incompleteness_theorems" target="_blank" rel="noopener">Курт Гёдель — Толымсыздық теоремалары</a></li></ul>]]></content:encoded>
      <pubDate>Fri, 17 Apr 2026 00:00:00 GMT</pubDate>
      <category>physics</category>
      <category>philosophy</category>
      <category>consciousness</category>
      <category>black-holes</category>
      <category>cosmology</category>
    </item>
    <item>
      <title>AI CMS-ті өлтірді — кем дегенде қарапайым сайттар үшін</title>
      <link>https://oleksiimazurenko.dev/kk/blog/ai-killed-cms-for-simple-sites</link>
      <guid isPermaLink="true">https://oleksiimazurenko.dev/kk/blog/ai-killed-cms-for-simple-sites</guid>
      <description>Дәстүрлі мазмұн басқару жүйелері портфолио, блог және лендингтер үшін неге қажетсіз болып барады — және AI құралдары бүкіл CMS қабатын табиғи тілмен қалай алмастырады.</description>
      <content:encoded><![CDATA[<h2>TL;DR</h2>
<p>Қарапайым сайттар үшін — портфолио, блог, лендинг, шағын бизнес сайттары — дәстүрлі CMS қажетсіз жүкке айналып барады. Claude Code, Cursor және GitHub Copilot сияқты AI құралдары енді код базасын тікелей өңдей алады, контексті түсінеді, мазмұнды аударады және өзгерістерді git арқылы деплой жасайды. CMS берген абстракция қабаты неғұрлым ақылды интерфейспен — табиғи тілмен — алмастырылып жатыр.</p>
<h2>Сіз төлеп жүрген CMS салығы</h2>
<p>Әрбір CMS жасырын құнмен келеді. Тек жазылым ақысы ғана емес — әйтпесе қарапайым сайтыңызды орап тұрған тұтас күрделілік экожүйесі:</p>
<ul><li><strong>Инфрақұрылым:</strong> Хостинг жасайтын дерекқор, қолдап отыратын API, қорғайтын басқару тақтасы. Тек WordPress-тің өзі вебтің ~43%-ын және CMS-ке бағытталған шабуылдардың ~90%-ын құрайды.</li><li><strong>Өнімділік:</strong> Беттерді динамикалық түрде генерациялау, әр сұрауда API шақырулары, CMS деректерін клиент жағында гидратациялау. Сіздің 3 беттік портфолиоңыз енді SaaS өнімінің архитектурасына ие.</li><li><strong>Жеткізушіге тәуелділік:</strong> Мазмұныңыз біреудің дерекқор схемасында тұрады. Contentful-дан Sanity-ге көшу керек пе? Бұл конфигурацияны өзгерту емес, бүтін бір жоба.</li><li><strong>Контексті ауыстыру:</strong> Кодты IDE-де өңдейсіз, содан кейін тақырыпты өзгерту үшін браузердегі CMS басқару тақтасына ауысасыз. Мәні бойынша бір операция үшін екі түрлі ойлау моделі.</li><li><strong>Құны:</strong> Headless CMS бағасы көбіне API шақырулары немесе мазмұн жазбалары санына қарай өседі. Жеке блогқа айына $99 тұратын мазмұн инфрақұрылымы қажет емес.</li></ul>
<p>Күн сайын 50 адам мазмұн өңдейтін маркетингтік сайт үшін бұл құн ақталады. Ал әзірлеушінің портфолиосы немесе шағын бизнестің лендингі үшін ше? Сіз шалшықтан өту үшін көпір салып жатырсыз.</p>
<h2>Не өзгерді: AI кодыңызды түсінеді</h2>
<p>CMS-тің болу себебі қарапайым еді: техникалық емес адамдарға (тіпті мазмұн өзгерту үшін кодқа тиіскісі келмейтін әзірлеушілерге де) сайттарды жаңарту үшін көрнекі интерфейс керек болды. Код тым күрделі, тым нәзік, тым оңай сынатын еді.</p>
<p>AI бұл теңдеуді түбегейлі өзгертті. Заманауи AI кодтау құралдары тек автотолықтырып қоймайды — олар жоба құрылымын түсінеді, бар паттерндерді оқиды және контекстке сай дұрыс өңдеулер жасайды. Жұмыс процесіндегі өзгеріс күрт:</p>
<pre><code># Old workflow: CMS
1. Open browser → log into CMS dashboard
2. Navigate to content → find the right page
3. Edit in WYSIWYG editor → fight with formatting
4. Preview → looks different from production
5. Hit publish → pray the cache invalidates

# New workflow: AI
1. Tell AI: &quot;Change the pricing on the landing page to $29/month&quot;
2. AI edits the file, you review the diff
3. Push to git → deploy</code></pre>
<p>Бұл болжам емес. Дәл қазір оқып отырған осы блог SolidStart-та жұмыс істейді, мазмұны TypeScript файлдарында сақталады. Әрбір мақала — оның ішінде осы да — AI-ға не жазу керегін айтып, нәтижесін тексеріп, git-ке push жасау арқылы жасалды. CMS басқару тақтасы жоқ. Дерекқор жоқ. Мазмұн мен код арасында API қабаты жоқ.</p>
<h2>Осы сайттан алынған нақты мысалдар</h2>
<p>Бұл сайт 10 тілді қолдайды, блогы бар, OG суреттерін динамикалық түрде генерациялайды, RSS арналары мен sitemap шығарады. Мазмұн қабаты былай көрінеді — таза TypeScript:</p>
<pre><code>// This blog post you&apos;re reading right now is a TypeScript file.
// No database. No API. No CMS dashboard.
// Just a typed object that AI can read and edit directly.

export const myPost: BlogPost = {
  slug: &quot;ai-killed-cms&quot;,
  date: &quot;2026-04-17&quot;,
  translations: {
    en: {
      title: &quot;Why I stopped using a CMS&quot;,
      description: &quot;AI understands my codebase better than any CMS UI.&quot;,
      content: makeContent(proseEn),
    },
    uk: { /* ... */ },
    de: { /* ... */ },
    // 10 languages — AI translates them all
  },
};</code></pre>
<p>AI арқылы жасайтын, бұрын CMS талап ететін істер:</p>
<ul><li><strong>Жаңа мақала қосу:</strong> "X туралы жаңа мақала жаз, бар посттардың құрылымын ұстан" — AI файлды жасайды, аудармаларын қосады, оны индекске тіркейді</li><li><strong>Лендинг мәтінін жаңарту:</strong> "Hero тақырыбын Y-ге өзгерт" — AI керек файлды тауып, жаңартады</li><li><strong>Мазмұнды аудару:</strong> "Бағалар бетіне неміс аудармасын қос" — AI ағылшын нұсқасын оқып, сөзбе-сөз емес, мәдени тұрғыдан бейімделген аударма шығарады</li><li><strong>Қатені түзету:</strong> "About бетінде қате бар, 'recieve' емес, 'receive' болу керек" — 3 секундта бітті, мағыналы хабарламамен git-ке коммит жасалды</li></ul>
<h2>CMS шын мәнінде нені шешті — және AI оны қалай алмастырады</h2>
<p>CMS не әкелгені және әрбір мүмкіндік AI жұмыс процесіне қалай сәйкес келетіні жайлы шыншыл болайық:</p>
<table><thead><tr><th>Мәселе</th><th>CMS шешімі</th><th>AI шешімі</th></tr></thead><tbody><tr><td>Техникалық емес өңдеу</td><td>WYSIWYG редакторы</td><td>Табиғи тілдегі нұсқаулар</td></tr><tr><td>Көптілді мазмұн</td><td>i18n плагиндері, локаль өрістері</td><td>AI мәдени контекспен аударады</td></tr><tr><td>Мазмұнды жоспарлау</td><td>Кірістірілген жариялау күндері</td><td>Кодтағы cron немесе күн өрістерімен git негізіндегі CI/CD</td></tr><tr><td>Нұсқалар тарихы</td><td>CMS ревизия жүйесі</td><td>Git — нұсқаларды бақылаудың алтын стандарты</td></tr><tr><td>Медиа басқару</td><td>Кірістірілген ресурстар кітапханасы</td><td>CDN + git LFS немесе бұлттық сақтау</td></tr></tbody></table>
<p>Басты түйін: git қазірдің өзінде кез келген CMS салғаннан гөрі жақсырақ нұсқаларды бақылау жүйесі. Ал табиғи тіл кез келген WYSIWYG редакторынан жақсырақ интерфейс — өйткені ол тек пішімдеуді емес, ниетті жеткізеді.</p>
<h2>Парадигманың ауысуы: код — мазмұн қабаты</h2>
<p>Біз инверсияны көріп отырмыз. Жиырма жыл бойы үрдіс мазмұнды кодтан бөлу болды — мазмұнды дерекқорға салу, оны API арқылы ашу, frontend-те рендерлеу. Кодты өңдеу қиын болып, мазмұнға техникалық емес адамдар қол жеткізуі керек болған кезде бұның мәні бар еді.</p>
<blockquote>AI жақсырақ CMS болу арқылы CMS-ті ескіртпеді. Ол кодты басқару тақтасындай қолжетімді ету арқылы CMS-ті ескіртті.</blockquote>
<p>Веб-мазмұнды басқарудың дамуы айқын траекторияны ұстанады:</p>
<ol><li><strong>2000-шы жылдар:</strong> Монолитті CMS (WordPress, Drupal) — мазмұн мен көрсетілім бір жүйеде байланысқан</li><li><strong>2010-шы жылдар:</strong> Headless CMS (Contentful, Strapi) — мазмұн API арқылы бөлінген, frontend фреймворктерімен рендерленеді</li><li><strong>2020-шы жылдар:</strong> Статикалық сайт генераторлары + Markdown (Hugo, Astro) — мазмұн файл ретінде, деплой кезінде құрастырылады</li><li><strong>2025+:</strong> Код-мазмұн ретінде + AI — мазмұн типтелген кодта тұрады, AI — өңдеу интерфейсі</li></ol>
<h2>CMS әлі де қажет болатын кездер</h2>
<p>Бұл "CMS өлді" деген пікір емес. CMS ауқымды деңгейде нақты мәселелерді шешеді. Ол әлі де қажет, егер:</p>
<ul><li><strong>Үлкен редакциялық командалар:</strong> Рөлге негізделген қатынауды, бекіту процестерін және бір мезгілде өңдеуді қажет ететін 10+ мазмұн редакторы. Git merge қақтығыстары — мазмұн редакторы шешетін мәселе емес.</li><li><strong>Жоғары жиілікті мазмұн:</strong> Күніне 50+ мақала жариялайтын жаңалық сайттарына git коммиттері емес, оңтайландырылған редакциялық пайплайндар керек.</li><li><strong>Күрделі мазмұн байланыстары:</strong> Мыңдаған SKU, өнім нұсқалары мен динамикалық баға белгілеуі бар электрондық коммерция каталогтарына құрылымдалған дерекқорлар қажет.</li><li><strong>Реттеуші талаптарға сай болу:</strong> Аудит іздерін, мазмұнды бекіту тізбектерін және заңмен бекітілген тексеру процестерін талап ететін салаларға арнайы жасалған жүйелер керек.</li></ul>
<p>Шекара айқын: егер мазмұн өзгерістеріңіз бірнеше техникалық емес мүдделі тараптардың жоғары жиілікте үйлесуін талап етсе, CMS өз күрделілігін ақтайды. Егер сіз жеке әзірлеуші, шағын команда болсаңыз немесе сағат сайын емес, апта сайын өзгеретін сайтты басқарсаңыз — AI + код қарапайымырақ, жылдамырақ, арзанырақ және сенімдірек.</p>
<h2>Болашақ: AI әмбебап интерфейс ретінде</h2>
<p>Үрдіс CMS шегінен асып түседі. "Негізгі жүйе тікелей әрекеттесуге тым күрделі" деген себеппен болған әрбір бағдарламалық абстракция қабатын AI сығымдап жатыр. Әкімшілік тақталары, конфигурация UI-лері, дерекқордың көрнекі редакторлары — бұлардың бәрі адам ниетін жүйелік өзгерістерге аударатын интерфейстер. AI бұл аударманы туа біткендей жасайды.</p>
<p>Қарапайым сайттар үшін болашақ әлдеқашан келді. Мазмұныңыз — код. Редакторыңыз — AI. Нұсқаларды бақылауыңыз — git. Деплойыңыз — push. Бүкіл CMS қабаты — басқару тақтасы, дерекқор, API, хостинг — сіздің ниетіңіз бен сайтыңыздың арасындағы аралық бағдарлама (middleware) болды. AI сол аралық бағдарламаға деген қажеттілікті жойды.</p>
<blockquote>Ең жақсы CMS — CMS-тің болмауы. Мазмұнды басқару маңызды емес болғандықтан емес — керісінше, AI кодтың өзін біз бұрын-соңды көрген ең түсінікті мазмұн басқару интерфейсіне айналдырғандықтан.</blockquote>]]></content:encoded>
      <pubDate>Fri, 17 Apr 2026 00:00:00 GMT</pubDate>
      <category>ai</category>
      <category>cms</category>
      <category>web-development</category>
      <category>opinion</category>
      <category>workflow</category>
    </item>
    <item>
      <title>Perplexity AI-ды Obsidian-ға MCP арқылы қалай қосу керек — жазбаларды тікелей чаттан</title>
      <link>https://oleksiimazurenko.dev/kk/blog/perplexity-obsidian-mcp-integration</link>
      <guid isPermaLink="true">https://oleksiimazurenko.dev/kk/blog/perplexity-obsidian-mcp-integration</guid>
      <description>Perplexity Desktop-ты MCP filesystem сервері арқылы Obsidian vault-ыңызды оқу және жазу үшін баптаңыз. Интернеттен зерттеп, бір әңгімеде жазбаларыңызға сақтаңыз.</description>
      <content:encoded><![CDATA[<h2>TL;DR</h2>
<p>Perplexity Desktop <strong>MCP (Model Context Protocol)</strong> коннекторларын қолдайды. Obsidian vault-ыңызға бағытталған ресми <code>@modelcontextprotocol/server-filesystem</code> серверін қосу арқылы Perplexity-ге табиғи тілде жазбаларды оқуды, жасауды және өңдеуді айта аласыз — тікелей чаттан. Плагиндерсіз, кеңейтімдерсіз, көшіріп-қоюсыз.</p>
<h2>Мәселе</h2>
<p>Perplexity зерттеуде тамаша — ол интернеттен іздейді, дереккөздерді қорытындылайды және сілтемелермен жауап береді. Бірақ осы нәтижелерді Obsidian vault-ыңызға сақтағыңыз келгенде жұмыс процесі бұзылады: мәтінді көшіресіз, Obsidian-ге ауысасыз, қажетті жазбаны табасыз, қоясыз, пішімдейсіз. Әр. Жолы.</p>
<p>"Perplexity to Obsidian" секілді браузер кеңейтімдері экспорттауға көмектеседі, бірақ олар бір бағытты — жасанды интеллект сіздің vault-ыңызды <em>көре</em> алмайды, бар жазбаларды оқи алмайды және қалталар құрылымына сүйеніп нені қайда қою керектігін шеше алмайды.</p>
<h2>MCP дегеніміз не?</h2>
<p><strong>Model Context Protocol (MCP)</strong> — жасанды интеллект модельдеріне жергілікті құралдармен және дерек көздерімен өзара әрекеттесуге мүмкіндік беретін ашық стандарт. Оны жасанды интеллектке арналған USB порт ретінде елестетіңіз — сіз "серверді" (шағын бағдарламаны) жалғайсыз, ал жасанды интеллект жаңа мүмкіндіктерге ие болады. Біздің жағдайда filesystem сервері Perplexity-ге файлдармен жұмыс істеу үшін 14 құрал береді:</p>
<pre><code>Perplexity Desktop (Mac App)
  │
  ├── MCP Connector: &quot;Obsidian Vault&quot;
  │     │
  │     └── @modelcontextprotocol/server-filesystem
  │           │
  │           ├── read_file()      → read any .md file
  │           ├── write_file()     → create or overwrite files
  │           ├── edit_file()      → patch existing files
  │           ├── list_directory() → browse vault structure
  │           ├── search_files()   → find files by pattern
  │           └── ... 14 tools total
  │
  └── Perplexity AI Model
        │
        └── &quot;Save this to my daily note&quot; → calls write_file()</code></pre>
<p>Негізгі түйін: жасанды интеллект моделі файлдарыңызға тікелей қол жеткізбейді. Ол сіздің құрылғыңызда жергілікті түрде жұмыс істейтін MCP сервері ұсынатын құралдарды шақырады. Жасанды интеллекттен деректермен бірдеңе істеуді нақты сұрамайынша, деректеріңіз компьютерден ешқашан шықпайды.</p>
<h2>Талаптар</h2>
<ul><li><strong>Perplexity Pro</strong> жазылымы (MCP коннекторлары ақылы пайдаланушыларға қолжетімді)</li><li>App Store-дан алынған <strong>Perplexity Mac App</strong> (браузер нұсқасы емес)</li><li>Mac-та орнатылған <strong>Node.js</strong> (<code>npx</code> жұмыс істеуі үшін)</li></ul>
<h2>Қадамдық баптау</h2>
<p>Бүкіл баптау шамамен 2 минут алады:</p>
<table><thead><tr><th>Step</th><th>Action</th></tr></thead><tbody><tr><td>1</td><td>Open &lt;strong&gt;Perplexity Mac App&lt;/strong&gt; (App Store version)</td></tr><tr><td>2</td><td>Click avatar → &lt;strong&gt;Settings&lt;/strong&gt; → &lt;strong&gt;Connectors&lt;/strong&gt;</td></tr><tr><td>3</td><td>Click &lt;strong&gt;Add Connector&lt;/strong&gt;</td></tr><tr><td>4</td><td>Set Server Name: &lt;code&gt;Obsidian Vault&lt;/code&gt;, Type: &lt;code&gt;Standard I/O&lt;/code&gt;</td></tr><tr><td>5</td><td>Paste the npx command with your vault path</td></tr><tr><td>6</td><td>Leave Environment Variables empty</td></tr><tr><td>7</td><td>Click &lt;strong&gt;Save&lt;/strong&gt; → confirm Security Warning</td></tr><tr><td>8</td><td>Verify status shows &lt;strong&gt;Running&lt;/strong&gt; with 14 tools</td></tr></tbody></table>
<h3>Команда</h3>
<p><strong>Command</strong> өрісіне қоятын команда:</p>
<pre><code>npx -y @modelcontextprotocol/server-filesystem &quot;/Users/yourname/Library/Mobile Documents/iCloud~md~obsidian/Documents/Obsidian Vault&quot;</code></pre>
<p>Жолды Obsidian vault-ыңыздың нақты орналасқан жерімен алмастырыңыз. Егер vault iCloud арқылы синхрондалса, жол <code>~/Library/Mobile Documents/iCloud~md~obsidian/Documents/</code> астында болады. Тырнақшаларды сақтап қалыңыз — жолда бос орындар болуы ықтимал.</p>
<h2>Оны қалай пайдалану керек</h2>
<p>Коннектор <strong>Running</strong> күйін 14 қолжетімді құралмен көрсеткеннен кейін, кез келген Perplexity чатына өтіп, vault-ыңызбен сөйлесе бастаңыз:</p>
<pre><code>&gt; Show me the structure of my Obsidian vault
&gt; Add a new reflection to daily notes/2026-04-12.md: &quot;Started using MCP today&quot;
&gt; Find all notes that mention &quot;meditation&quot;
&gt; Create a new note in concepts/ about quantum computing
&gt; List all files in my ideas/ folder</code></pre>
<p>Жасанды интеллект сіздің vault құрылымыңызды түсінеді, пішімдеу келісімдеріңізді құрметтейді және бар мазмұнмен жұмыс істей алады. Одан тақырыпты интернеттен зерттеп, қорытындыны нақты жазбаға тікелей сақтауды сұрай аласыз — Perplexity іздеуін Obsidian ұйымдастыруымен бір ағында біріктіресіз.</p>
<h2>MCP неге басқа тәсілдерден асып түседі</h2>
<p>MCP-ге дейін Perplexity мен Obsidian-ды байланыстырудың шектеулі жолдары болды:</p>
<table><thead><tr><th>Feature</th><th>Copy-paste</th><th>Browser extension</th><th>MCP integration</th></tr></thead><tbody><tr><td>AI sees vault structure</td><td>No</td><td>No</td><td>&lt;strong&gt;Yes&lt;/strong&gt;</td></tr><tr><td>AI reads existing notes</td><td>No</td><td>No</td><td>&lt;strong&gt;Yes&lt;/strong&gt;</td></tr><tr><td>AI writes to vault</td><td>No</td><td>Export only</td><td>&lt;strong&gt;Yes&lt;/strong&gt;</td></tr><tr><td>AI edits existing files</td><td>No</td><td>No</td><td>&lt;strong&gt;Yes&lt;/strong&gt;</td></tr><tr><td>Works from chat</td><td>No</td><td>Partially</td><td>&lt;strong&gt;Yes&lt;/strong&gt;</td></tr><tr><td>Setup complexity</td><td>None</td><td>Low</td><td>Medium (one-time)</td></tr></tbody></table>
<h2>Ағымдағы шектеулер</h2>
<ul><li><strong>Тек Mac</strong> — Perplexity MCP коннекторлары қазіргі уақытта тек Mac App Store нұсқасында жұмыс істейді</li><li><strong>Obsidian API интеграциясы жоқ</strong> — filesystem сервері файлдармен тікелей жұмыс істейді, Obsidian API арқылы емес. Бұл файлдарды жасау кезінде Obsidian плагиндерін (Linter, Templater) іске қоспайтынын білдіреді</li><li><strong>Растау қажет</strong> — құпия файл операциялары Perplexity қолданбасында сіздің растауыңызды талап етуі мүмкін — бұл қауіпсіздік функциясы, қате емес</li></ul>
<h2>Қорытынды</h2>
<p>Бұл баптау Perplexity-ді зерттеу құралынан зерттеу-мен-сақтау құралына айналдырады:</p>
<ol><li>Интернеттен зерттеп, бір әңгімеде Obsidian-ге сақтаңыз</li><li>Жасанды интеллект сіздің vault құрылымыңызды көреді және ұйымдастыру жүйеңізге бейімделеді</li><li>Қолданбалар арасында ауысу жоқ — бәрі Perplexity чатында болады</li></ol>
<h2>Дереккөздер</h2>
<ul><li><a href="https://modelcontextprotocol.io" target="_blank" rel="noopener noreferrer">Model Context Protocol</a> — official MCP specification</li><li><a href="https://github.com/modelcontextprotocol/servers/tree/main/src/filesystem" target="_blank" rel="noopener noreferrer">MCP Filesystem Server</a> — the server used in this setup</li><li><a href="https://www.perplexity.ai/help-center/en/articles/11502712-local-and-remote-mcps-for-perplexity" target="_blank" rel="noopener noreferrer">Perplexity Help Center</a> — Local and Remote MCPs for Perplexity</li><li><a href="https://www.perplexity.ai/hub/blog/everything-is-computer" target="_blank" rel="noopener noreferrer">Perplexity Blog</a> — Everything is Computer</li></ul>]]></content:encoded>
      <pubDate>Sun, 12 Apr 2026 00:00:00 GMT</pubDate>
      <category>Obsidian</category>
      <category>Perplexity</category>
      <category>MCP</category>
      <category>AI</category>
      <category>Productivity</category>
    </item>
    <item>
      <title>Claude Code CLI мен Obsidian арқылы күнделікті AI жаңалықтар дайджесті — нөл тәуелділік</title>
      <link>https://oleksiimazurenko.dev/kk/blog/ai-news-digest-claude-code-obsidian</link>
      <guid isPermaLink="true">https://oleksiimazurenko.dev/kk/blog/ai-news-digest-claude-code-obsidian</guid>
      <description>6 жолдық bash-скрипт, Claude Code headless режимі және macOS launchd арқылы күнделікті жаңалық зерттеу агентін қалай құрастырғаным. Ол 11 тақырыпты іздеп, дайджесттерді Obsidian-ға тікелей жазады.</description>
      <content:encoded><![CDATA[<h2>TL;DR</h2>
<p>Таң сайын сағат 9:00-де <strong>Claude Code CLI</strong> утилитасын headless режимінде іске қосатын 6 жолдан тұратын bash-скрипт. Ол 11 бапталатын тақырып бойынша интернеттен жаңалықтарды іздейді, шуды сүзгіден өткізеді және пішімделген markdown-дайджестті iCloud арқылы синхрондалатын <strong>Obsidian vault</strong>-қа тікелей жазады. Нөл тәуелділік. Барлығы ~100 жол конфигурация.</p>
<h2>Мәселе</h2>
<p>Әзірлеуші ретінде бірнеше технологиядан хабардар болып отыру — күнделікті салық сияқты. RSS-арналар шулы, Twitter уақытты жеп қояды, ал жаңалық хаттар дәл ағын үстінде терең отырғаныңда келеді. Маған зерттеу жұмысын <em>өзімнің орныма</em> істейтін және нәтижелерді мен әлдеқашан жұмыс істейтін жерде — Obsidian vault-та көрсететін нәрсе керек болды.</p>
<p>Әдеттегі шешім — скрапинг құбырын құру: жоспарлаушы, краулер, NLP-құбыр, дерекқор, хабарландыру қызметі. Бұл сайт өзінің HTML-ын өзгерткенде сынуы мүмкін нәрсе үшін апталап кететін жұмыс. Маған бір түстен кейін құрастыруға болатын нәрсе керек еді.</p>
<h2>Архитектура</h2>
<p>Бүкіл жүйе — 4 файл және нөл тәуелділік. Міне, ол басынан аяғына дейін қалай жұмыс істейді:</p>
<pre><code>macOS launchd (9:00 AM daily)
  │
  └── digest.sh
        │
        └── claude -p &quot;$(cat prompt.md)&quot; --max-turns 20 --allowedTools Read,WebSearch,WebFetch,Write
              │
              ├── Reads topics.yaml (11 configurable topics)
              ├── WebSearch → finds news for each topic (last 24-48h)
              ├── WebFetch → reads full articles
              ├── Filters noise: old tutorials, promos, AI spam
              └── Write → saves digest to Obsidian Vault
                    │
                    └── ~/Obsidian Vault/digests/2026-04-12.md
                          │
                          └── iCloud sync → available on all devices</code></pre>
<h2>Код (толығымен)</h2>
<p>Жоба әдейі барынша ықшам етіп жасалған. Әр жол өз орнын ақтайды.</p>
<h3>Кіру нүктесі: digest.sh</h3>
<p>Бүкіл қосымша — 6 жолдан тұратын bash-скрипт:</p>
<pre><code>#!/bin/bash
DIGEST_DIR=&quot;$HOME/Developer/news-digest&quot;

claude -p &quot;$(cat &quot;$DIGEST_DIR/prompt.md&quot;)&quot; \
  --max-turns 20 \
  --allowedTools Read,WebSearch,WebFetch,Write</code></pre>
<p>Негізгі жалаушалар: <code>-p</code> Claude-ты headless режимінде іске қосады (интерактивті терминалсыз), <code>--max-turns 20</code> агентке барлық тақырыпты зерттеуге жеткілікті орын береді, ал <code>--allowedTools</code> агентті тек файлдарды оқумен, интернеттен іздеумен және нәтижені жазумен шектейді.</p>
<h3>Ми: prompt.md</h3>
<p>Ақыл-ой осында тұрады. Промпт Claude-ты нені іздеу, қалай сүзу және қайда сақтау керектігі туралы нақты нұсқаулықтары бар жаңалық зерттеу агентіне айналдырады:</p>
<pre><code># News Digest Agent

You are a news research agent. Your job is to find today&apos;s most important
and interesting news for a senior frontend developer.

## Instructions

1. Read the topics file at ~/Developer/news-digest/topics.yaml
2. For EACH topic, search the web for news from the last 24-48 hours
3. Filter: only include genuinely new and noteworthy items
4. Write the digest as a markdown file to Obsidian Vault digests/YYYY-MM-DD.md
5. IMPORTANT: Use the Write tool to save the file. Do NOT output to stdout.

## Rules

- Language: Ukrainian for summaries, English for titles and technical terms
- If there&apos;s no real news for a topic — SKIP IT ENTIRELY
- Prioritize: releases &gt; breaking changes &gt; security &gt; new patterns &gt; discussions
- Max 5 items per topic, sorted by importance
- Include direct links to sources
- Skip promotional content, generic tutorials, and AI-generated spam</code></pre>
<h3>Конфигурация: topics.yaml</h3>
<p>Тақырыптар толығымен бапталады — жаңа тақырып қосасың да, ол ертеңгі дайджестке енеді. Әр тақырыптың аты және AI іздеуін бағыттайтын қосымша контексі болады:</p>
<pre><code>topics:
  - name: better-auth
    context: &quot;auth library for TypeScript. New releases, breaking changes&quot;

  - name: Next.js
    context: &quot;GitHub issues, releases, App Router, Turbopack, performance&quot;

  - name: SolidJS
    context: &quot;SolidStart, releases, ecosystem, comparison with React&quot;

  - name: Tailwind CSS
    context: &quot;v4 updates, new utilities, plugins&quot;

  - name: Claude AI
    context: &quot;Anthropic announcements, Claude Code, new models, MCP, API&quot;

  - name: GPT AI
    context: &quot;OpenAI announcements, new models, ChatGPT features&quot;

  - name: React
    context: &quot;React 19+, Server Components, new patterns, ecosystem&quot;

  - name: Apple
    context: &quot;Hardware, software, WWDC, developer tools, Apple Intelligence&quot;

  - name: Notable People
    context: &gt;
      Latest tweets from: Elon Musk, Dario Amodei, Sam Altman,
      Jensen Huang, Andrej Karpathy, Simon Willison, Swyx...

  - name: AI Global
    context: &quot;Major AI news, new models, regulations, open-source AI&quot;</code></pre>
<h2>launchd арқылы жоспарлау</h2>
<p>macOS-те <code>launchd</code> — қайталанатын тапсырмаларды жоспарлаудың табиғи тәсілі (Linux-тегі cron сияқты). plist-файл скрипттің қашан және қалай іске қосылатынын анықтайды:</p>
<pre><code>&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;
&lt;!DOCTYPE plist PUBLIC &quot;-//Apple//DTD PLIST 1.0//EN&quot;
  &quot;http://www.apple.com/DTDs/PropertyList-1.0.dtd&quot;&gt;
&lt;plist version=&quot;1.0&quot;&gt;
&lt;dict&gt;
    &lt;key&gt;Label&lt;/key&gt;
    &lt;string&gt;com.news-digest&lt;/string&gt;

    &lt;key&gt;ProgramArguments&lt;/key&gt;
    &lt;array&gt;
        &lt;string&gt;/bin/bash&lt;/string&gt;
        &lt;string&gt;/Users/you/Developer/news-digest/digest.sh&lt;/string&gt;
    &lt;/array&gt;

    &lt;key&gt;StartCalendarInterval&lt;/key&gt;
    &lt;dict&gt;
        &lt;key&gt;Hour&lt;/key&gt;
        &lt;integer&gt;9&lt;/integer&gt;
        &lt;key&gt;Minute&lt;/key&gt;
        &lt;integer&gt;0&lt;/integer&gt;
    &lt;/dict&gt;

    &lt;key&gt;StandardOutPath&lt;/key&gt;
    &lt;string&gt;/Users/you/Developer/news-digest/logs/stdout.log&lt;/string&gt;
    &lt;key&gt;StandardErrorPath&lt;/key&gt;
    &lt;string&gt;/Users/you/Developer/news-digest/logs/stderr.log&lt;/string&gt;

    &lt;key&gt;EnvironmentVariables&lt;/key&gt;
    &lt;dict&gt;
        &lt;key&gt;PATH&lt;/key&gt;
        &lt;string&gt;/usr/local/bin:/opt/homebrew/bin:/usr/bin:/bin&lt;/string&gt;
        &lt;key&gt;HOME&lt;/key&gt;
        &lt;string&gt;/Users/you&lt;/string&gt;
    &lt;/dict&gt;
&lt;/dict&gt;
&lt;/plist&gt;</code></pre>
<p>Оны <code>launchctl load ~/Library/LaunchAgents/com.news-digest.plist</code> арқылы орнатасың. Скрипт күн сайын сағат 9:00-де іске қосылады, тіпті жүйеден шығып кеткен болсаң да — launchd жүйе оянған кезде өткізіп алынған тапсырмаларды орындайды.</p>
<h2>Нәтиже қалай көрінеді</h2>
<p>Таң сайын Obsidian vault-та құрылымдалған әрі маңыздылығы бойынша сұрыпталған жаңалықтары бар жаңа markdown-файл пайда болады:</p>
<pre><code>---
date: 2026-04-12
---

# News Digest — 2026-04-12

## Next.js

### Next.js 16.1 Released with Improved Turbopack Caching
Нова версія Next.js 16.1 включає покращене кешування для Turbopack,
що зменшує час холодного старту на ~40%.
[Посилання](https://nextjs.org/blog/next-16-1)

### Critical Memory Leak Fix in App Router
Виправлено витік пам&apos;яті в App Router при частому перемиканні
між динамічними маршрутами.
[GitHub Issue](https://github.com/vercel/next.js/issues/...)

## Claude AI

### Claude Code 1.5 — MCP Server Auto-Discovery
Anthropic випустив оновлення Claude Code з автоматичним
виявленням MCP серверів у проєкті.
[Блог](https://www.anthropic.com/news/...)</code></pre>
<h2>Сандармен</h2>
<table><thead><tr><th>Metric</th><th>Value</th></tr></thead><tbody><tr><td>Total files in project</td><td>&lt;strong&gt;4&lt;/strong&gt; (digest.sh, prompt.md, topics.yaml, .gitignore)</td></tr><tr><td>Lines of code</td><td>&lt;strong&gt;~100&lt;/strong&gt;</td></tr><tr><td>External dependencies</td><td>&lt;strong&gt;0&lt;/strong&gt;</td></tr><tr><td>Setup time</td><td>&lt;strong&gt;~10 minutes&lt;/strong&gt;</td></tr><tr><td>Daily execution time</td><td>&lt;strong&gt;2-5 minutes&lt;/strong&gt;</td></tr><tr><td>Cost per run</td><td>&lt;strong&gt;~$0.10-0.30&lt;/strong&gt; (Claude API usage)</td></tr></tbody></table>
<h2>Негізгі дизайн шешімдері</h2>
<ul><li><strong>API орнына Claude Code CLI</strong> — API-кілттерді, HTTP-клиенттерді басқарудың немесе жауаптарды талдаудың қажеті жоқ. CLI аутентификацияны, құралдарды орындауды және қателерден қалпына келтіруді өз бетінше атқарады</li><li><strong>Email орнына Obsidian</strong> — дайджесттерді іздеуге, оларға сілтеме жасауға болады және олар мәңгі сақталады. Олар кіріс жәшігінде тұншығудың орнына менің жазбаларымның қасында тұрады</li><li><strong>cron орнына launchd</strong> — launchd macOS-тің табиғи жоспарлаушысы. Ол өткізіп алынған іске қосуларды, журналдауды және орта айнымалыларын таза өңдейді</li><li><strong>Тақырыптарға YAML</strong> — жаңа тақырып қосу — 2 жолдық өзгеріс. Кодты өзгертудің қажеті жоқ</li><li><strong>Бос тақырыптарды аттап өту</strong> — нақты жаңалық болмаса, бөлім мүлдем қосылмайды. Толтырғышсыз, жасанды созылған мазмұнсыз</li></ul>
<h2>Өзіңдікін қалай құрастыруға болады</h2>
<p>Оны 10 минутта іске қоса аласың:</p>
<ol><li><a href="https://docs.anthropic.com/en/docs/claude-code" target="_blank" rel="noopener noreferrer">Claude Code CLI</a> орнатып, аутентификациядан өт</li><li>Репозиторийді клонда: <code>git clone https://github.com/oleksiimazurenko/news-digest</code></li><li><code>topics.yaml</code>-ты өз қызығушылықтарыңмен, ал <code>prompt.md</code>-ты сақтау жолыңмен өңде</li><li>plist-файлды өз пайдаланушы атыңмен және жолдарыңмен өңдеп, содан кейін оны <code>launchctl load</code> жаса</li><li>Сағат 9:00-ді күт — немесе <code>bash digest.sh</code> арқылы қолмен тексер</li></ol>
<h2>Қорытынды</h2>
<p>Бұл жобадағы ең қызық нәрсе — онда <em>жоқ</em> нәрселер. Дерекқор жоқ, API-сервер жоқ, Docker-контейнер жоқ, npm-пакеттер жоқ, Python virtualenv жоқ, HTML-парсер жоқ, NLP-құбыр жоқ. Тек промпт, тақырыптар тізімі және барлық ауыр жұмысты AI-агентке тапсыратын 6 жолдық скрипт.</p>
<p>AI-агенттермен құрастыру тәжірибеде осылай көрінеді: сен <em>нені</em> және <em>қайда</em> екенін анықтайсың, ал агент <em>қалай</em> екенін атқарады. Әзірлеудің жалпы уақыты шамамен 2 сағат болды, оның ішінде сүзу сапасын дұрыстау үшін промптпен жүргізілген итерациялар бар.</p>
<h2>Дереккөздер</h2>
<ul><li><a href="https://github.com/oleksiimazurenko/news-digest" target="_blank" rel="noopener noreferrer">news-digest on GitHub</a> — full source code</li><li><a href="https://docs.anthropic.com/en/docs/claude-code" target="_blank" rel="noopener noreferrer">Claude Code Documentation</a> — headless mode and CLI flags</li><li><a href="https://developer.apple.com/library/archive/documentation/MacOSX/Conceptual/BPSystemStartup/Chapters/CreatingLaunchdJobs.html" target="_blank" rel="noopener noreferrer">Apple Developer</a> — creating launchd jobs</li><li><a href="https://obsidian.md" target="_blank" rel="noopener noreferrer">Obsidian</a> — markdown knowledge base</li></ul>]]></content:encoded>
      <pubDate>Sun, 12 Apr 2026 00:00:00 GMT</pubDate>
      <category>Claude Code</category>
      <category>AI</category>
      <category>Obsidian</category>
      <category>Automation</category>
      <category>Productivity</category>
    </item>
    <item>
      <title>Next.js Dark Mode: жыпылықтаусыз және React 19 ескертулерінсіз</title>
      <link>https://oleksiimazurenko.dev/kk/blog/nextjs-dark-mode-without-flash</link>
      <guid isPermaLink="true">https://oleksiimazurenko.dev/kk/blog/nextjs-dark-mode-without-flash</guid>
      <description>Next.js 15+ жүйесінде жыпылықтаусыз қараңғы режим үшін next-themes орнына Zustand + useServerInsertedHTML қалай қолдану керек, React 19 скрипт ескертулерінсіз.</description>
      <content:encoded><![CDATA[<h2>TL;DR</h2>
<p><code>next-themes</code> тема жыпылықтауын (FOUC) болдырмау үшін React Client Component ішінде <code>&lt;script&gt;</code> тегін рендерлейді. React 19 енді бұл туралы ескертеді — әрі оны басудың ешқандай жолы жоқ. Кітапхана 2025 жылдың наурызынан бері жаңартылмаған. Шешім: <code>next-themes</code> орнына Zustand store + <code>useServerInsertedHTML</code> қолданып, скриптті React ағашынан тыс инъекциялау. Нөл жаңа тәуелділік. Нөл FOUC. Нөл ескерту.</p>
<h2>Мәселе</h2>
<p>Егер сіз <code>next-themes</code> кітапханасын Next.js 15+ және React 19 нұсқаларымен қолдансаңыз, әр бет жүктелген сайын консольде мына қатені көресіз:</p>
<pre><code>Encountered a script tag while rendering React component.
Scripts inside React components are never executed when rendering on the client.
Consider using template tag instead.

src/providers/theme-provider.tsx (7:10) @ ThemeProvider</code></pre>
<p>Бұл гидратация сәйкессіздігі емес. Бұл — клиентте React компоненттері рендерлеген <code>&lt;script&gt;</code> тегтері <strong>ешқашан орындалмайтыны</strong> туралы React 19-дың айқын ескертуі. Скрипт SSR кезінде жұмыс істейді (ол HTML ішінде бар), бірақ React оны қате деп белгілейді.</p>
<h2>Неге бұлай болады</h2>
<p><code>next-themes</code> React гидратациялаудан бұрын <code>&lt;html&gt;</code> элементіне дұрыс тема класын орнатуы қажет — әйтпесе қате теманың жыпылықтауы пайда болады. Ол үшін ол <code>React.createElement</code> арқылы inline <code>&lt;script&gt;</code> инъекциялайды:</p>
<pre><code>// next-themes renders a &lt;script&gt; inside a Client Component
return React.createElement(Provider, { value },
  React.createElement(&quot;script&quot;, {
    suppressHydrationWarning: true,
    dangerouslySetInnerHTML: { __html: `(...theme init code...)` }
  }),
  children
)</code></pre>
<p>React 19 өз әрекетін өзгертті: компоненттер ішіндегі script тегтері енді айқын белгіленеді. React 19-ға дейін бұл үнсіз еленбейтін. Скрипттегі <code>suppressHydrationWarning</code> пропы көмектеспейді — ол гидратация ескертулерін басады, ал «компонент ішіндегі script» ескертуін емес.</p>
<h2>Біз нені сынап көрдік (және неге сәтсіз болды)</h2>
<p>Шешімді табу алдында біз әрбір тәсілді жүйелі түрде сынап көрдік:</p>
<table><thead><tr><th>Attempt</th><th>Result</th></tr></thead><tbody><tr><td>&lt;code&gt;suppressHydrationWarning&lt;/code&gt; on &lt;code&gt;&amp;lt;html&amp;gt;&lt;/code&gt;</td><td>Suppresses hydration mismatch, but NOT the script tag warning</td></tr><tr><td>Delay mount with &lt;code&gt;useState&lt;/code&gt; + &lt;code&gt;useEffect&lt;/code&gt;</td><td>Warning disappears, but causes FOUC (theme flash)</td></tr><tr><td>Raw &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; in layout &lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt;</td><td>React 19 catches it — same warning even in Server Components</td></tr><tr><td>&lt;code&gt;next/script&lt;/code&gt; with &lt;code&gt;beforeInteractive&lt;/code&gt;</td><td>Still rendered inside React tree — same warning</td></tr><tr><td>Remove &lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt;, put Script in body</td><td>Same warning — it&apos;s inside &lt;code&gt;&amp;lt;html&amp;gt;&lt;/code&gt; which is React-managed</td></tr><tr><td>&lt;code&gt;next-themes@1.0.0-beta.0&lt;/code&gt;</td><td>Beta, no release date, unclear if fixed</td></tr></tbody></table>
<h2>Шешім: Zustand + useServerInsertedHTML</h2>
<p><code>useServerInsertedHTML</code> — бұл HTML-ды SSR ағынына <strong>React компоненттер ағашынан тыс</strong> инъекциялайтын Next.js хук. Скрипт HTML ішіне түседі, бірақ React оны клиенттік рендеринг кезінде ешқашан «көрмейді» — сондықтан ешқандай ескерту жоқ. Реактивті тема күйі үшін Zustand store-мен біріктірілгенде, біз тәуелділіксіз толық алмастырғышқа қол жеткіземіз.</p>
<h3>Ол қалай жұмыс істейді</h3>
<pre><code>SSR (server):
  useServerInsertedHTML → injects &lt;script&gt; into HTML stream
  ↓
  Browser receives HTML with theme script already in it
  ↓
  Script runs BEFORE React hydrates → correct class on &lt;html&gt;
  ↓
  No flash. No mismatch. No warning.

Client (after hydration):
  useEffect → _init() → Zustand store syncs with localStorage
  ↓
  useTheme() → reactive theme state for components</code></pre>
<h3>1-қадам: Zustand store</h3>
<p>Store тема күйін басқарады, DOM-ға класстар қолданады, жүйелік теманы анықтауды өңдейді және қойындылар арасында синхрондайды. <code>_init()</code> әдісі <code>useEffect</code> ішінде қолдануға арналған тазалау функциясын қайтарады:</p>
<pre><code>import { create } from &quot;zustand&quot;;

const STORAGE_KEY = &quot;theme&quot;;
const MEDIA_QUERY = &quot;(prefers-color-scheme: dark)&quot;;

function getSystemTheme(): &quot;light&quot; | &quot;dark&quot; {
  return window.matchMedia(MEDIA_QUERY).matches ? &quot;dark&quot; : &quot;light&quot;;
}

function applyTheme(resolved: string, disableTransition: boolean) {
  const d = document.documentElement;

  if (disableTransition) {
    const style = document.createElement(&quot;style&quot;);
    style.appendChild(
      document.createTextNode(
        &quot;*,*::before,*::after{transition:none!important}&quot;
      )
    );
    document.head.appendChild(style);
    window.getComputedStyle(document.body);
    setTimeout(() =&gt; document.head.removeChild(style), 1);
  }

  d.classList.remove(&quot;light&quot;, &quot;dark&quot;);
  d.classList.add(resolved);
  d.style.colorScheme = resolved;
}

interface ThemeState {
  theme: string;
  systemTheme: &quot;light&quot; | &quot;dark&quot;;
  resolvedTheme: string;
  setTheme: (theme: string) =&gt; void;
  _init: (disableTransition: boolean) =&gt; () =&gt; void;
}

export const useThemeStore = create&lt;ThemeState&gt;((set, get) =&gt; ({
  theme: &quot;system&quot;,
  systemTheme: &quot;light&quot;,
  resolvedTheme: &quot;light&quot;,

  setTheme: (newTheme) =&gt; {
    const { systemTheme, disableTransitionOnChange } = get();
    const resolved = newTheme === &quot;system&quot; ? systemTheme : newTheme;
    localStorage.setItem(STORAGE_KEY, newTheme);
    applyTheme(resolved, disableTransitionOnChange);
    set({ theme: newTheme, resolvedTheme: resolved });
  },

  _init: (disableTransition) =&gt; {
    const stored = localStorage.getItem(STORAGE_KEY) || &quot;system&quot;;
    const system = getSystemTheme();
    const resolved = stored === &quot;system&quot; ? system : stored;

    set({ theme: stored, systemTheme: system, resolvedTheme: resolved });
    applyTheme(resolved, disableTransition);

    // System theme changes
    const mq = window.matchMedia(MEDIA_QUERY);
    const onChange = (e: MediaQueryListEvent) =&gt; {
      const newSystem = e.matches ? &quot;dark&quot; : &quot;light&quot;;
      const { theme } = get();
      set({ systemTheme: newSystem,
            resolvedTheme: theme === &quot;system&quot; ? newSystem : theme });
      if (theme === &quot;system&quot;) applyTheme(newSystem, disableTransition);
    };
    mq.addEventListener(&quot;change&quot;, onChange);

    // Cross-tab sync
    const onStorage = (e: StorageEvent) =&gt; {
      if (e.key !== STORAGE_KEY || !e.newValue) return;
      const system = getSystemTheme();
      const resolved = e.newValue === &quot;system&quot; ? system : e.newValue;
      set({ theme: e.newValue, resolvedTheme: resolved });
      applyTheme(resolved, disableTransition);
    };
    window.addEventListener(&quot;storage&quot;, onStorage);

    return () =&gt; {
      mq.removeEventListener(&quot;change&quot;, onChange);
      window.removeEventListener(&quot;storage&quot;, onStorage);
    };
  },
}));

export function useTheme() {
  return useThemeStore((s) =&gt; ({
    theme: s.theme,
    setTheme: s.setTheme,
    resolvedTheme: s.resolvedTheme,
    systemTheme: s.systemTheme,
  }));
}</code></pre>
<h3>2-қадам: ThemeProvider</h3>
<p>Provider екі нәрсе жасайды: <code>useServerInsertedHTML</code> арқылы FOUC-тың алдын алу скриптін инъекциялайды және монтаждау кезінде Zustand store-ды инициализациялайды:</p>
<pre><code>&quot;use client&quot;

import { useEffect } from &quot;react&quot;
import { useServerInsertedHTML } from &quot;next/navigation&quot;
import { useThemeStore } from &quot;@/store/use-theme-store&quot;

const THEME_INIT_SCRIPT = `(function(){
  try {
    var t = localStorage.getItem(&quot;theme&quot;) || &quot;system&quot;;
    var r = t;
    if (t === &quot;system&quot;) {
      r = window.matchMedia(&quot;(prefers-color-scheme: dark)&quot;).matches
        ? &quot;dark&quot; : &quot;light&quot;;
    }
    document.documentElement.classList.remove(&quot;light&quot;, &quot;dark&quot;);
    document.documentElement.classList.add(r);
    document.documentElement.style.colorScheme = r;
  } catch(e) {}
})()`

export function ThemeProvider({
  children,
  disableTransitionOnChange = false,
}) {
  // Injects script into SSR HTML outside the React tree
  // — prevents FOUC without triggering React 19 warning
  useServerInsertedHTML(() =&gt; (
    &lt;script dangerouslySetInnerHTML={{ __html: THEME_INIT_SCRIPT }} /&gt;
  ))

  useEffect(() =&gt; {
    return useThemeStore.getState()._init(disableTransitionOnChange)
  }, [disableTransitionOnChange])

  return &lt;&gt;{children}&lt;/&gt;
}</code></pre>
<h3>3-қадам: Layout</h3>
<pre><code>import { ThemeProvider } from &quot;@/providers/theme-provider&quot;

export default function RootLayout({ children }) {
  return (
    &lt;html lang=&quot;en&quot; suppressHydrationWarning&gt;
      &lt;body&gt;
        &lt;ThemeProvider disableTransitionOnChange&gt;
          {children}
        &lt;/ThemeProvider&gt;
      &lt;/body&gt;
    &lt;/html&gt;
  )
}</code></pre>
<h3>4-қадам: Оны қолдану</h3>
<pre><code>import { useTheme } from &quot;@/store/use-theme-store&quot;

export function ThemeSwitch() {
  const { theme, setTheme } = useTheme();

  return (
    &lt;button onClick={() =&gt; setTheme(
      theme === &quot;dark&quot; ? &quot;light&quot; : &quot;dark&quot;
    )}&gt;
      {theme === &quot;dark&quot; ? &quot;☀️&quot; : &quot;🌙&quot;}
    &lt;/button&gt;
  );
}</code></pre>
<h2>next-themes-тен көшу</h2>
<p>API әдейі бірдей жасалған. Көшу — әр файлда бір ғана импортты өзгерту:</p>
<pre><code>- import { useTheme } from &quot;next-themes&quot;
+ import { useTheme } from &quot;@/store/use-theme-store&quot;

  // API is identical — no other changes needed
  const { theme, setTheme, resolvedTheme, systemTheme } = useTheme()</code></pre>
<h2>Салыстыру</h2>
<table><thead><tr><th></th><th>next-themes</th><th>Zustand + useServerInsertedHTML</th></tr></thead><tbody><tr><td>React 19 warning</td><td>Yes — &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; in client component</td><td>No</td></tr><tr><td>FOUC prevention</td><td>Yes</td><td>Yes</td></tr><tr><td>Cross-tab sync</td><td>Yes</td><td>Yes</td></tr><tr><td>System theme detection</td><td>Yes</td><td>Yes</td></tr><tr><td>&lt;code&gt;disableTransitionOnChange&lt;/code&gt;</td><td>Yes</td><td>Yes</td></tr><tr><td>Bundle size</td><td>~3.5 KB</td><td>~1.5 KB (uses existing Zustand)</td></tr><tr><td>Dependencies</td><td>+1 (next-themes)</td><td>0 (Zustand already in project)</td></tr><tr><td>Maintenance risk</td><td>Abandoned since March 2025</td><td>Your own code</td></tr></tbody></table>
<h2>Неге басқа баламалар емес?</h2>
<h3>@wrksz/themes</h3>
<p>Ол да <code>useServerInsertedHTML</code> қолданатын тікелей алмастырғыш. Жұмыс істейді, бірақ бұл — бір ғана мейнтейнерден шыққан тағы бір тәуелділік. Егер <code>next-themes</code> бізге бірдеңе үйретсе — тәуелділіктер тасталады. ~100 жол кодпен сіз шешімді толығымен өзіңіздікі ете аласыз.</p>
<h3>next-themes@1.0.0-beta.0</h3>
<p>npm-де бар, бірақ шығарылым күнінсіз, changelog-сыз және React 19 ескертуі түзетілгені туралы нақты нұсқаусыз. Өндірістік кодты мерзімсіз бета-нұсқаға тігу — алуға тұрарлық тәуекел емес.</p>
<h3>Тек CSS (prefers-color-scheme)</h3>
<p>Жүйелік теманы анықтауға жарайды, бірақ пайдаланушы таңдауын сақтауды (localStorage), теманы қолмен ауыстыруды немесе «system» опциясын өңдей алмайды. Ол үшін JavaScript қажет.</p>
<h2>Қорытынды</h2>
<ol><li><code>next-themes</code> нақты түрде тасталған — соңғы шығарылым 2025 жылдың наурызы, React 19 ескертуі түзетілмеген</li><li><code>useServerInsertedHTML</code> — React ескертулерінсіз скрипт инъекциялауға арналған дұрыс Next.js примитиві</li><li>Zustand Context провайдеріне қарағанда азырақ кодпен реактивті тема күйін қамтамасыз етеді</li><li>Бүкіл шешім — ~100 жол, нөл жаңа тәуелділік, әрі әр жолды өзіңіз иеленесіз</li></ol>
<h2>Дереккөздер</h2>
<ul><li><a href="https://github.com/shadcn-ui/ui/issues/10104" target="_blank" rel="noopener noreferrer">shadcn/ui #10104</a> — Dark mode guide should address React 19 script warning</li><li><a href="https://github.com/pacocoursey/next-themes/issues/296" target="_blank" rel="noopener noreferrer">next-themes #296</a> — React 19 support request (open since 2024)</li><li><a href="https://github.com/facebook/react/issues/34008" target="_blank" rel="noopener noreferrer">React #34008</a> — Script tags not executing in components (by design)</li><li><a href="https://dev.to/wrksz/i-was-tired-of-next-themes-being-abandoned-so-i-built-a-drop-in-replacement-4dbk" target="_blank" rel="noopener noreferrer">@wrksz/themes</a> — Drop-in replacement using useServerInsertedHTML</li><li><a href="https://nextjs.org/docs/app/building-your-application/styling/css#server-inserted-html" target="_blank" rel="noopener noreferrer">Next.js Docs</a> — useServerInsertedHTML API</li></ul>]]></content:encoded>
      <pubDate>Sat, 11 Apr 2026 00:00:00 GMT</pubDate>
      <category>next.js</category>
      <category>react-19</category>
      <category>dark-mode</category>
      <category>zustand</category>
      <category>performance</category>
    </item>
    <item>
      <title>Next.js 16-да рендерлеуді бөгейтін 94 CSS файлын нашар құжатталған Turbopack мүмкіндігімен қалай жойдым</title>
      <link>https://oleksiimazurenko.dev/kk/blog/eliminating-render-blocking-css</link>
      <guid isPermaLink="true">https://oleksiimazurenko.dev/kk/blog/eliminating-render-blocking-css</guid>
      <description>experimental.inlineCss-тен MutationObserver хактарына дейін әрбір тәсілді сынап көрген бірнеше күннен кейін мен Next.js App Router-дегі рендерлеуді бөгейтін CSS мәселесін шешетін Turbopack Import Attributes мүмкіндігін таптым.</description>
      <content:encoded><![CDATA[<h2>Мәселе</h2>
<p>Біздің қосымша (<a href="https://promova.com" target="_blank" rel="noopener noreferrer">Promova</a>) Next.js 16-ны <strong>Landing Builder</strong> — маркетингтік беттерді ~90 түрлі секция компоненттерінен (hero-лар, FAQ-тар, баға, пікірлер т.б.) құрастыратын CMS негізіндегі жүйемен бірге қолданады. Архитектура секция атауларын <code>next/dynamic()</code> шақыруларына сәйкестендіретін <code>sectionRegistry.tsx</code> файлын пайдаланады:</p>
<pre><code>// sectionRegistry.tsx — imports all 90 sections
export const sectionRegistry = {
  HeroSection: dynamic(() =&gt; import(&apos;./HeroSection/HeroSection&apos;)),
  FAQSection: dynamic(() =&gt; import(&apos;./FAQSection/FAQSection&apos;)),
  ReviewsSection: dynamic(() =&gt; import(&apos;./ReviewsSection/ReviewsSection&apos;)),
  // ... 87 more
}</code></pre>
<p>Бір лендинг беті небәрі <strong>5-8 секцияны</strong> ғана рендерлейді. Бірақ Lighthouse мынаны көрсетті:</p>
<pre><code>Eliminate render-blocking resources
  94 CSS resources (~330 KB)
  Potential savings: 5,440 ms</code></pre>
<p><strong>Неге?</strong> Turbopack барлық 90 <code>import()</code> жолын қолжетімді деп санайды және <strong>әрбір</strong> SCSS модулі үшін <code>&lt;link rel="stylesheet"&gt;</code> жасайды. Тіпті бетте ешқашан рендерленбейтін секциялардың да CSS-і <code>&lt;head&gt;</code> ішіне енгізіледі. Бұл — Next.js App Router-дің <a href="https://github.com/vercel/next.js/issues/62485" target="_blank" rel="noopener noreferrer">расталған, күтілетін әрекеті</a>. CSS Server Components-тан келетін <code>dynamic()</code> импорттары үшін кодқа бөлінбейді. Мейнтейнерлер мұны FOUC-тың алдын алу үшін жасалған әдейі ымыраға келу деп есептейді. <strong>Түзету жоспарланбаған.</strong></p>
<h2>Мен сынап көрген барлық әдіс (және неге сәтсіз аяқталғаны)</h2>
<p>Мен таба алатын әрбір тәсілді қарастырып, бірнеше күн жұмсадым. Міне толық тізім:</p>
<table><thead><tr><th>Approach</th><th>Why it doesn&apos;t work</th></tr></thead><tbody><tr><td>Split &lt;code&gt;sectionRegistry&lt;/code&gt; into per-section files</td><td>CSS still loaded — &lt;a href=&quot;https://github.com/vercel/next.js/issues/61066&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;#61066&lt;/a&gt;</td></tr><tr><td>&lt;code&gt;experimental.inlineCss: true&lt;/code&gt;</td><td>Inlines CSS on &lt;strong&gt;every SSR request&lt;/strong&gt; — crashed our CMS under load</td></tr><tr><td>&lt;code&gt;experimental.optimizeCss&lt;/code&gt; (Critters)</td><td>&lt;a href=&quot;https://github.com/vercel/next.js/issues/62485&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Incompatible with streaming&lt;/a&gt; in App Router</td></tr><tr><td>CSS-in-JS rewrite</td><td>Rewriting 90 sections from SCSS to CSS-in-JS is not realistic</td></tr><tr><td>&lt;code&gt;media=&quot;print&quot;&lt;/code&gt; hack</td><td>Doesn&apos;t work with CSS Modules in Turbopack — &lt;code&gt;&amp;lt;link&amp;gt;&lt;/code&gt; tags are managed by the framework</td></tr><tr><td>Switch back to Webpack</td><td>Has &lt;code&gt;experiments.css&lt;/code&gt; options, but we&apos;re committed to Turbopack</td></tr><tr><td>Turbopack plugins</td><td>&lt;a href=&quot;https://github.com/vercel/next.js/discussions/85465&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Don&apos;t exist&lt;/a&gt; — no plugin API</td></tr><tr><td>Turbopack CSS loaders</td><td>Not supported — only JS output</td></tr><tr><td>SWC plugins for CSS</td><td>SWC only processes JavaScript</td></tr><tr><td>Client Component wrapper for &lt;code&gt;dynamic()&lt;/code&gt;</td><td>Registry is a global constant — bundler sees all dependencies</td></tr><tr><td>Next.js middleware HTML rewrite</td><td>&lt;a href=&quot;https://github.com/orgs/vercel/discussions/3874&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Middleware can&apos;t modify response body&lt;/a&gt;</td></tr><tr><td>Suspense + streaming with async import</td><td>React Float always pulls CSS into initial &lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt;</td></tr><tr><td>Suspense + 3s delay</td><td>Content streams later, but CSS is in the initial &lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt; chunk</td></tr><tr><td>&lt;code&gt;experimental.cssChunking&lt;/code&gt;</td><td>Already default. Merges chunks but doesn&apos;t remove irrelevant CSS</td></tr><tr><td>Post-build Beasties/Critters</td><td>Only for static export, &lt;a href=&quot;https://github.com/vercel/next.js/discussions/59989&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;not with ISR&lt;/a&gt;</td></tr><tr><td>npm libraries (next-critical, fg-loadcss)</td><td>Pages Router only or abandoned (6+ years)</td></tr><tr><td>&lt;code&gt;inlineCss&lt;/code&gt; per-route exclusion</td><td>&lt;a href=&quot;https://github.com/vercel/next.js/pull/72195&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Not supported&lt;/a&gt; — all-or-nothing global flag</td></tr><tr><td>Turbopack &lt;code&gt;as: &apos;raw&apos;&lt;/code&gt; for SCSS</td><td>Returns &lt;code&gt;undefined&lt;/code&gt; instead of text</td></tr><tr><td>Turbopack rule for &lt;code&gt;*.inline.module.scss&lt;/code&gt;</td><td>Turbopack intercepts &lt;code&gt;.module.scss&lt;/code&gt; &lt;strong&gt;before&lt;/strong&gt; custom rules</td></tr><tr><td>Turbopack rule for &lt;code&gt;*.inline.scss&lt;/code&gt;</td><td>Turbopack intercepts &lt;code&gt;.scss&lt;/code&gt; &lt;strong&gt;before&lt;/strong&gt; custom rules</td></tr><tr><td>MutationObserver &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt;</td><td>Tested in dev, risky — CSS still loads as separate files, possible FOUC</td></tr></tbody></table>
<h3>inlineCss тұзағы</h3>
<p>Next.js-те барлық <code>&lt;link rel="stylesheet"&gt;</code> тегтерін inline <code>&lt;style&gt;</code> тегтерімен ауыстыратын <a href="https://github.com/vercel/next.js/pull/72195" target="_blank" rel="noopener noreferrer"><code>experimental.inlineCss</code></a> жалауы бар. Тамаша естіледі, солай ма?</p>
<p>Мәселе мынада: бұл <strong>бәрі не ештеңе</strong>. Оны маршрут бойынша қосуға болмайды. Егер сізде SSR (<code>force-dynamic</code>) беттер болса, әрбір сұраныс барлық CSS-ті inline түрде қайта құрады. Біз оны сынап көрдік — біздің headless CMS жүктемеге шыдамай, істен шықты. Мұның қауіпсіз жұмыс істеуі үшін <strong>беттеріңіздің 100%-ы</strong> <code>force-static</code> немесе ISR болуы керек. 20-дан астам SSR беті (аутентификация, бақылау тақталары, динамикалық беттер) болғанда бұл — ауқымды миграция.</p>
<h2>Ашылым: Turbopack Import Attributes</h2>
<p><a href="https://nextjs.org/blog/next-16-2-turbopack#inline-loader-configuration" target="_blank" rel="noopener noreferrer">Next.js 16.2 шығарылым жазбаларын</a> ақтарып отырып, мен нашар құжатталған мүмкіндікті таптым: <strong>Turbopack Import Attributes</strong>. Ол TC39 <code>with {}</code> синтаксисін қолданып, нақты бір импорт үшін бұрыннан бар бандлер конвейерін ауыстыруға мүмкіндік береді:</p>
<pre><code>import { cssText, styles } from &apos;./hero.module.scss&apos; with {
  turbopackLoader: &apos;@promova/scss-to-inline-loader&apos;,
  turbopackAs: &apos;*.js&apos;
}</code></pre>
<p>Бұл Turbopack-ке былай дейді: <em>"Бұл импортты стиль кестесі ретінде өңдеме. Оны менің арнайы loader-ым арқылы өткізіп, нәтижесін JavaScript ретінде қарастыр."</em></p>
<p><strong>Бұл — түйінді түсінік.</strong> Turbopack рендерлеуді бөгейтін <code>&lt;link rel="stylesheet"&gt;</code> жасаудың орнына, біздің loader SCSS-ті компиляциялап, оны JS жолы ретінде экспорттайды. Содан кейін біз оны компоненттің ішіне тікелей inline <code>&lt;style&gt;</code> тегі ретінде енгіземіз. Нәтиже: <strong>бетке нақты рендерленетін секциялардың CSS-і ғана бет HTML-іне түседі</strong>. 5 секциясы бар бет 94 емес, 5 секцияға арналған CSS алады.</p>
<h2>Шешім</h2>
<h3>1. Арнайы Turbopack Loader</h3>
<p>yarn workspace пакеті ретіндегі ~70 жолдық Node.js скрипті (<code>@promova/scss-to-inline-loader</code>):</p>
<pre><code>const { createHash } = require(&apos;node:crypto&apos;)
const sass = require(&apos;sass&apos;)
const postcss = require(&apos;postcss&apos;)
const postcssModules = require(&apos;postcss-modules&apos;)
const path = require(&apos;path&apos;)

const SHARED_STYLES = path.resolve(__dirname, &apos;../shared/styles&apos;)

// Same prependData as sassOptions.prependData in next.config.ts
const PREPEND_DATA = `
@use &quot;sass:math&quot; as math;
@use &quot;sass:list&quot; as list;
@use &quot;${SHARED_STYLES}/_variables.scss&quot; as *;
@use &quot;${SHARED_STYLES}/_breakpoints.scss&quot; as *;
@use &quot;${SHARED_STYLES}/_mixins.scss&quot; as *;
`

module.exports = function scssToInlineLoader(source) {
  const callback = this.async()
  processScss(source, this.resourcePath)
    .then((js) =&gt; callback(null, js))
    .catch((err) =&gt; callback(err))
}

async function processScss(source, resourcePath) {
  // 1. Compile SCSS → CSS (with global variables, mixins, breakpoints)
  const sassResult = sass.compileString(PREPEND_DATA + &apos;\n&apos; + source, {
    loadPaths: [path.dirname(resourcePath), SHARED_STYLES],
    style: &apos;compressed&apos;,
    sourceMap: false,
    url: new URL(&apos;file://&apos; + resourcePath),
  })

  // 2. Scope class names with postcss-modules (CSS Modules compatible)
  let classNames = {}
  const result = await postcss([
    postcssModules({
      generateScopedName(name, filename) {
        const file = path.basename(filename, path.extname(filename))
          .replace(&apos;.module&apos;, &apos;&apos;)
        const hash = createHash(&apos;md5&apos;)
          .update(filename + name)
          .digest(&apos;base64url&apos;)
          .slice(0, 6)
        return `${file}__${name}__${hash}`
      },
      getJSON(_, json) { classNames = json },
    }),
  ]).process(sassResult.css, { from: resourcePath })

  // 3. Export as JS module — same interface as CSS Modules
  return [
    `export const cssText = ${JSON.stringify(result.css)};`,
    `export const styles = ${JSON.stringify(classNames)};`,
    `export default styles;`,
  ].join(&apos;\n&apos;)
}</code></pre>
<p>Ол не істейді: <strong><code>styles</code></strong> — стандартты CSS Modules-тегідей аймақталған класс атауларының картасы. <strong><code>cssText</code></strong> — жол ретіндегі компиляцияланған CSS.</p>
<h3>2. InlineStyle компоненті</h3>
<p>Автоматты дедупликация үшін React 19-дың кірістірілген <code>&lt;style href precedence&gt;</code> API-ін пайдаланады:</p>
<pre><code>export function InlineStyle({ css, id }: { css: string; id: string }) {
  return (
    &lt;style href={id} precedence=&quot;default&quot;&gt;
      {css}
    &lt;/style&gt;
  )
}</code></pre>
<p>React 19 кепілдік береді: бірдей <code>href</code> → DOM-да тек бір <code>&lt;style&gt;</code>. Егер секция бетте екі рет рендерленсе, CSS бәрібір бір рет қана енгізіледі.</p>
<h3>3. Компонент бойынша миграция (секциясына ~6 жол)</h3>
<pre><code>// BEFORE: Turbopack → &lt;link rel=&quot;stylesheet&quot;&gt; (render-blocking)
import styles from &apos;./hero_section.module.scss&apos;</code></pre>
<pre><code>// AFTER: Custom loader → JS module → inline &lt;style&gt; (non-blocking)
import { cssText, styles } from &apos;./hero_section.module.scss&apos; with {
  turbopackLoader: &apos;@promova/scss-to-inline-loader&apos;,
  turbopackAs: &apos;*.js&apos;
}
import { InlineStyle } from &apos;@promova/scss-to-inline-loader/InlineStyle&apos;

export function HeroSection() {
  return (
    &lt;section className={styles.hero}&gt;
      &lt;InlineStyle css={cssText} id=&quot;hero-section&quot; /&gt;
      {/* ... component JSX, className usage is identical */}
    &lt;/section&gt;
  )
}</code></pre>
<p><strong><code>.module.scss</code> файлдары дәл сол күйінде қалады.</strong> CSS-ті қайта жазу жоқ. Stylelint, Prettier, IDE қолдауы — бәрі сақталады.</p>
<h2>Неге бұл inlineCss: true-ден жақсы</h2>
<p>Міне басты айырмашылық:</p>
<table><thead><tr><th></th><th>&lt;code&gt;inlineCss: true&lt;/code&gt;</th><th>Import Attributes + Loader</th></tr></thead><tbody><tr><td>What gets inlined</td><td>&lt;strong&gt;ALL CSS on the page&lt;/strong&gt; (94 files)</td><td>&lt;strong&gt;Only CSS for rendered sections&lt;/strong&gt; (5-8 files)</td></tr><tr><td>SSR overhead</td><td>CSS rebuilt on every request</td><td>CSS compiled at build time</td></tr><tr><td>Per-route control</td><td>No (all-or-nothing)</td><td>Yes (per-import)</td></tr><tr><td>SSR pages safety</td><td>Risky (overloads server)</td><td>Safe (component-level)</td></tr></tbody></table>
<p><code>inlineCss: true</code> кезінде бет бәрібір БАРЛЫҚ 94 стиль кестесін inline түрде алады — олар жай ғана <code>&lt;link&gt;</code> тегтерінің орнына <code>&lt;style&gt;</code> тегтері. HTML ісінеді. Біздің тәсілмен <strong>тек нақты рендерленетін CSS қана HTML-ге түседі</strong>. 5 секция рендерленді → 5 <code>&lt;style&gt;</code> тегі. Болды.</p>
<h2>Turbopack тұзағы: .module.scss үшін глобалды ережелер жоқ</h2>
<p>Мен түскен бір тұзақ: loader-ды глобалды түрде қолдану үшін <code>next.config.ts</code> ішіне Turbopack ережесін қосуға болады деп ойлауыңыз мүмкін:</p>
<pre><code>// ❌ THIS CAUSES A FATAL PANIC
turbopack: {
  rules: {
    &apos;**/components/**/*.module.scss&apos;: {
      loaders: [&apos;@promova/scss-to-inline-loader&apos;],
      as: &apos;*.js&apos;
    }
  }
}</code></pre>
<p><strong>Мұны істемеңіз.</strong> Turbopack-тің кірістірілген CSS модуль конвейері арнайы ережелер қолданылмас <strong>бұрын</strong> <code>.module.scss</code> файлдарын ұстап алады. Ол <code>as: '*.js'</code> шығысымен қайшы келетін <code>EcmascriptCssModule</code> активін жасайды, бұл мынаған әкеледі:</p>
<pre><code>FATAL PANIC: inner asset should be CSS processable</code></pre>
<p><code>with {}</code> атрибуттары жұмыс істейді, өйткені олар Turbopack-ке <strong>импорт орнында</strong> CSS модуль конвейерін толығымен айналып өтуді тапсырады. Мұны конфигурация арқылы глобалды түрде жасаудың жолы жоқ.</p>
<h2>Нәтижелер</h2>
<p>Landing Builder бойынша 127 секция компоненті көшірілді. Өндірістік құрастыру тексерілді.</p>
<table><thead><tr><th>Metric</th><th>Before</th><th>After</th></tr></thead><tbody><tr><td>Render-blocking CSS files</td><td>94</td><td>0 (for landing sections)</td></tr><tr><td>CSS in HTML per page</td><td>~330 KB (all sections)</td><td>~20-40 KB (rendered sections only)</td></tr><tr><td>CSS delivery</td><td>&lt;code&gt;&amp;lt;link&amp;gt;&lt;/code&gt; (network request, blocking)</td><td>&lt;code&gt;&amp;lt;style&amp;gt;&lt;/code&gt; (inline, non-blocking)</td></tr><tr><td>Sass features</td><td>Full</td><td>Full (variables, mixins, nesting, @use)</td></tr><tr><td>CSS Modules scoping</td><td>Built-in</td><td>postcss-modules (compatible)</td></tr><tr><td>Code change per section</td><td>—</td><td>~6 lines (import + InlineStyle)</td></tr></tbody></table>
<h2>Шектеулер</h2>
<ul><li><strong>Импорт сайын <code>with {}</code> ұзақ-сонар</strong> — әрбір импортқа 3 қосымша жол қажет. Оны глобалды түрде қолданудың жолы жоқ (жоғарыдағы тұзақты қараңыз).</li><li><strong>Тек Turbopack</strong> — <code>with {}</code> атрибуттарын Webpack қолдамайды. Егер Webpack қосалқы нұсқасы қажет болса, импорттар стандартты CSS Modules-ке қайтады (<code>cssText</code> <code>undefined</code> болады).</li><li><strong>Класс атауларын хештеу</strong> — біздің loader Turbopack-тің кірістірілгенінен өзге хештеу алгоритмін қолданады. Класс атаулары DevTools-та басқаша көрінеді. Функционалды тұрғыдан бірдей.</li><li><strong>HTML өлшемі ұлғаяды</strong> — CSS бөлек файлдар ретінде кэштелудің орнына HTML ішінде inline болады. CDN-нен берілетін статикалық/ISR беттер үшін бұл мәселе емес.</li></ul>
<h2>Мұны қашан қолдану керек</h2>
<p>Бұл әдіс мына жағдайларда неғұрлым тиімді:</p>
<ol><li><strong>Сізде registry/barrel үлгісі бар</strong> — бір файл көптеген компоненттерді импорттайды, бірақ бетте тек бірнешеуі ғана рендерленеді</li><li><strong>Сіз Turbopack-тесіз</strong> — Import Attributes Turbopack-ке тән</li><li><strong>Сізге компонент бойынша бақылау керек</strong> — бәрі-не-ештеңе жалауы емес</li><li><strong>Сіздің SCSS күрделі</strong> — айнымалылар, mixin-дер, breakpoint-тер, ұялау — бәрі қолдау табады</li><li><strong>Сіз <code>experimental.inlineCss</code>-ті қолдана алмайсыз</strong> — өйткені сізде SSR беттер бар немесе егжей-тегжейлі бақылау қалайсыз</li></ol>
<h2>Қатысты GitHub Issues</h2>
<p>Егер сіз Next.js App Router-дегі рендерлеуді бөгейтін CSS-тен зардап шексеңіз — сіз жалғыз емессіз:</p>
<h3>Негізгі мәселе</h3>
<ul><li><a href="https://github.com/vercel/next.js/issues/62485" target="_blank" rel="noopener noreferrer"><strong>#62485</strong></a> — Render blocking CSS (maintainers: "expected behavior")</li><li><a href="https://github.com/vercel/next.js/issues/61066" target="_blank" rel="noopener noreferrer"><strong>#61066</strong></a> — Dynamic imports from Server Components are not code-split</li><li><a href="https://github.com/vercel/next.js/issues/54935" target="_blank" rel="noopener noreferrer"><strong>#54935</strong></a> — Server-side dynamic imports don't split client modules</li><li><a href="https://github.com/vercel/next.js/issues/61574" target="_blank" rel="noopener noreferrer"><strong>#61574</strong></a> — JS/CSS code splitting doesn't work as documented</li><li><a href="https://github.com/vercel/next.js/issues/57634" target="_blank" rel="noopener noreferrer"><strong>#57634</strong></a> — Add support for critical CSS inlining with App Router</li><li><a href="https://github.com/vercel/next.js/issues/50300" target="_blank" rel="noopener noreferrer"><strong>#50300</strong></a> — next/dynamic on server component does not build CSS modules</li></ul>
<h3>inlineCss мүмкіндігі және мәселелер</h3>
<ul><li><a href="https://github.com/vercel/next.js/pull/72195" target="_blank" rel="noopener noreferrer"><strong>PR #72195</strong></a> — experimental: css inlining (the implementation)</li><li><a href="https://github.com/vercel/next.js/pull/73182" target="_blank" rel="noopener noreferrer"><strong>PR #73182</strong></a> — Don't inline CSS in RSC payload for client navigation</li><li><a href="https://github.com/vercel/next.js/issues/75648" target="_blank" rel="noopener noreferrer"><strong>#75648</strong></a> — WhatsApp preview broken with inlineCss + Tailwind</li><li><a href="https://github.com/vercel/next.js/issues/83612" target="_blank" rel="noopener noreferrer"><strong>#83612</strong></a> — Turbopack wrong font URL with inline CSS</li></ul>
<h3>Қауымдастық шешім іздеуде</h3>
<ul><li><a href="https://github.com/vercel/next.js/discussions/82894" target="_blank" rel="noopener noreferrer"><strong>Discussion #82894</strong></a> — How to prevent render-blocking with CSS Modules? (2025)</li><li><a href="https://github.com/vercel/next.js/discussions/70526" target="_blank" rel="noopener noreferrer"><strong>Discussion #70526</strong></a> — Ideas for reducing render-blocking CSS</li><li><a href="https://github.com/vercel/next.js/discussions/59814" target="_blank" rel="noopener noreferrer"><strong>Discussion #59814</strong></a> — Render blocking styles with Tailwind</li><li><a href="https://github.com/vercel/next.js/discussions/49691" target="_blank" rel="noopener noreferrer"><strong>Discussion #49691</strong></a> — How to deal with render-blocking modular CSS</li><li><a href="https://github.com/vercel/next.js/discussions/59989" target="_blank" rel="noopener noreferrer"><strong>Discussion #59989</strong></a> — Critical CSS inlining with App Router</li><li><a href="https://github.com/vercel/next.js/discussions/80486" target="_blank" rel="noopener noreferrer"><strong>Discussion #80486</strong></a> — Is optimizeCss still in use?</li><li><a href="https://github.com/vercel/next.js/discussions/85465" target="_blank" rel="noopener noreferrer"><strong>Discussion #85465</strong></a> — Turbopack plugin API (doesn't exist)</li></ul>
<h3>Turbopack CSS қателері</h3>
<ul><li><a href="https://github.com/vercel/next.js/issues/68412" target="_blank" rel="noopener noreferrer"><strong>#68412</strong></a> — Incorrect CSS modules load order with external components</li><li><a href="https://github.com/vercel/next.js/issues/76464" target="_blank" rel="noopener noreferrer"><strong>#76464</strong></a> — Turbopack sometimes strips SCSS imports</li><li><a href="https://github.com/vercel/next.js/issues/82497" target="_blank" rel="noopener noreferrer"><strong>#82497</strong></a> — SCSS module not loading when not-found.tsx is present</li><li><a href="https://github.com/vercel/next.js/issues/88544" target="_blank" rel="noopener noreferrer"><strong>#88544</strong></a> — Exporting Sass variables from CSS modules doesn't work with Turbopack</li></ul>
<p>Миллиондаған пайдаланушыға қызмет көрсететін тіл үйрену платформасы <a href="https://promova.com" target="_blank" rel="noopener noreferrer">Promova</a>-да жасалған. Егер сіз дәл осындай CSS мәселесімен күресіп жатсаңыз, жоғарыдағы GitHub issue-ларында осы мақалаға сілтеме жасаудан тартынбаңыз.</p>]]></content:encoded>
      <pubDate>Wed, 08 Apr 2026 00:00:00 GMT</pubDate>
      <category>Next.js</category>
      <category>CSS</category>
      <category>Performance</category>
      <category>Turbopack</category>
      <category>SCSS</category>
    </item>
    <item>
      <title>Next.js Memory Leak: Fetch + Standalone режимі — түзетусіз 2 жыл</title>
      <link>https://oleksiimazurenko.dev/kk/blog/nextjs-memory-leak-fetch-standalone</link>
      <guid isPermaLink="true">https://oleksiimazurenko.dev/kk/blog/nextjs-memory-leak-fetch-standalone</guid>
      <description>Next.js глобалды fetch-ке патч салып, әр сұрауда жад ағызатын кэш қабатын қосады. Docker/K8s-те бұл бірнеше сағат сайын OOM құлауына әкеледі. Қате Next.js 14-тен бері бар және 16.2.x-те әлі шешілмеген.</description>
      <content:encoded><![CDATA[<h2>TL;DR</h2>
<p>Next.js жаһандық <code>fetch</code>-ке патч салып, жауап деректеріне босатылуы тиіс болғаннан кейін де сілтемелерді ұстап тұратын кэш қабатын қосады. Әрбір <code>fetch</code> шақыруы GC-ге ешқашан қайтарылмайтын жадты қосады. Docker/Kubernetes-те бұл бірнеше сағат сайын OOM құлауына әкеледі. Бұл қате Next.js 14-тен (2024 жылдың сәуірі) бері бар және 16.2.x-те (2026 жылдың наурызы) әлі шешілмеген. Vercel-де мәселе эфемерлі serverless функцияларының арқасында білінбейді.</p>
<h2>Қалыпты fetch қалай жұмыс істейді</h2>
<pre><code>Request → fetch → got data → response to user → GC cleans up → memory free</code></pre>
<h2>Next.js-те fetch қалай жұмыс істейді</h2>
<p>Next.js жаһандық <code>fetch</code>-ті ұстап алып, оны өзінің кэш/трекинг қабатымен орайды:</p>
<pre><code>Request → Next.js fetch wrapper → creates:
  - Performance entry (speed tracking)    ← not cleaned
  - Request metadata object               ← not cleaned
  - Internal promise wrapper              ← not cleaned
  - Cache lookup entry                    ← not cleaned
  - Response body (for unique URL)        ← not cleaned
→ Response to user
→ GC sees objects are still referenced → doesn&apos;t touch them
→ Memory grows indefinitely</code></pre>
<h2>Продакшнде не болады</h2>
<pre><code>Self-hosted (Docker/K8s):
  Request 1:      fetch → +100KB → RAM: 100KB
  Request 2:      fetch → +100KB → RAM: 200KB
  Request 1000:   fetch → +100KB → RAM: 100MB
  Request 50,000: fetch → +100KB → RAM: 5GB → OOM Kill 💀
  → Kubernetes restarts the pod → cycle repeats

Vercel (Serverless):
  Request 1: [start] → fetch → +100KB → [process dies] → 0 KB ✅
  Request 2: [start] → fetch → +100KB → [process dies] → 0 KB ✅
  → Memory never accumulates</code></pre>
<h2>Зардап шеккен нұсқалар</h2>
<h3>Next.js — App Router бар барлық нұсқалар</h3>
<table><thead><tr><th>Version</th><th>Issue</th><th>Date</th></tr></thead><tbody><tr><td>14.2.x</td><td>&lt;a href=&quot;https://github.com/vercel/next.js/issues/64212&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;#64212&lt;/a&gt;</td><td>April 2024</td></tr><tr><td>14.x-15.x</td><td>&lt;a href=&quot;https://github.com/vercel/next.js/issues/68578&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;#68578&lt;/a&gt;</td><td>August 2024</td></tr><tr><td>14.3.0-canary</td><td>&lt;a href=&quot;https://github.com/vercel/next.js/issues/79588&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;#79588&lt;/a&gt;</td><td>May 2025</td></tr><tr><td>16.0.1</td><td>&lt;a href=&quot;https://github.com/vercel/next.js/issues/85914&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;#85914&lt;/a&gt;</td><td>November 2025</td></tr><tr><td>16.1.0</td><td>&lt;a href=&quot;https://github.com/vercel/next.js/discussions/88603&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;#88603&lt;/a&gt;</td><td>January 2026</td></tr><tr><td>16.0.10</td><td>&lt;a href=&quot;https://github.com/vercel/next.js/issues/90433&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;#90433&lt;/a&gt;</td><td>February 2026</td></tr><tr><td>16.2.0-canary.51</td><td>Confirmed in #90433 comments</td><td>March 2026</td></tr></tbody></table>
<h3>Node.js</h3>
<p>Node.js 20, 22, 24, 25-те сынақтан өтті — барлығында ағады.</p>
<h2>Не жұмыс істемейді</h2>
<table><thead><tr><th>Attempt</th><th>Result</th></tr></thead><tbody><tr><td>&lt;code&gt;cacheMaxMemorySize: 0&lt;/code&gt;</td><td>Doesn&apos;t help — leak is not from this cache</td></tr><tr><td>Disable image optimization</td><td>Doesn&apos;t help</td></tr><tr><td>&lt;code&gt;--max-old-space-size=6144&lt;/code&gt;</td><td>Just crashes slower</td></tr><tr><td>Read response via &lt;code&gt;.json()&lt;/code&gt; / &lt;code&gt;.text()&lt;/code&gt;</td><td>Doesn&apos;t help</td></tr><tr><td>Remove React fetch patching (canary.45)</td><td>Doesn&apos;t help</td></tr><tr><td>Upgrade to latest version</td><td>Doesn&apos;t help (leaks on 16.2.0-canary.51 too)</td></tr></tbody></table>
<h2>Не жұмыс істейді (уақытша шешімдер)</h2>
<table><thead><tr><th>Workaround</th><th>Why it helps</th></tr></thead><tbody><tr><td>Replace &lt;code&gt;fetch&lt;/code&gt; with &lt;code&gt;axios&lt;/code&gt;</td><td>Bypasses Next.js wrapper</td></tr><tr><td>Replace &lt;code&gt;fetch&lt;/code&gt; with &lt;code&gt;node-fetch&lt;/code&gt;</td><td>Same reason</td></tr><tr><td>Downgrade Node.js to 20.15.1</td><td>Older undici has fewer leaks (for some)</td></tr><tr><td>Docker image &lt;code&gt;node:20-alpine3.21&lt;/code&gt;</td><td>Helps in some cases</td></tr><tr><td>Deploy to Lambda (SST + OpenNext)</td><td>Ephemeral functions — memory doesn&apos;t accumulate</td></tr><tr><td>Deploy to Vercel</td><td>Same — serverless</td></tr></tbody></table>
<h3>axios уақытша шешімінің шектеуі</h3>
<p>Өз API сұрауларыңды axios-пен алмастыруға болады. Бірақ Next.js ішкі түрде патч салынған fetch-ті мыналар үшін пайдаланады:</p>
<ul><li>ISR (Incremental Static Regeneration)</li><li><code>revalidatePath</code> / <code>revalidateTag</code></li><li>Дедупликациямен Server Components деректерін алу</li><li><code>use cache</code> (Next.js 16)</li></ul>
<p>Яғни кодыңда бірде-бір <code>fetch</code> болмаса да — Next.js оны бәрібір астыртын пайдаланады.</p>
<h2>Vercel неге түзетпейді</h2>
<h3>Бизнес-логика</h3>
<p>Vercel — Next.js хостингінен ақша табатын компания. Олардың платформасында мәселе білінбейді (serverless = эфемерлі). Қате тек self-hosted-ке (Docker, K8s, VPS) — яғни Vercel-ге төлемейтіндерге ғана әсер етеді.</p>
<h3>Ресми ұстаным</h3>
<p>Tim Neutkens (Vercel мейнтейнері) мәселені талдап, бұл Next.js емес, <strong>undici</strong>-дің (Node.js fetch кітапханасы) мәселесі деп мәлімдеді. Issue #90433 жабылды. Мынаған қарамастан:</p>
<ul><li>сол Node.js-те axios пен node-fetch ағусыз жұмыс істейді</li><li>Ағу тек fetch Next.js орамасынан өткенде ғана пайда болады</li><li>Қате 2 жыл бойы түзетілмей ашық тұр</li></ul>
<h3>Басымдықтар</h3>
<p>Осы 2 жылда Next.js командасы мыналарды шығарды:</p>
<ul><li>Turbopack (2-5x жылдам build) — маркетингтік артықшылық</li><li>Cache Components / <code>use cache</code> — Vercel серверлеріне жүктемені азайтады</li><li>middleware орнына <code>proxy.ts</code> — Vercel-де edge-деплойды жеңілдетеді</li><li>DevTools MCP — AI хайпы</li></ul>
<p>Self-hosted-тегі memory leak? Басымдықта емес.</p>
<h2>Шешім: AWS Lambda (SST + OpenNext)</h2>
<h3>Бұл не</h3>
<p>OpenNext — Next.js build-ін AWS Lambda пішіміне түрлендіретін ашық бастапқы кодты адаптер. SST — инфрақұрылымды автоматтандыратын фреймворк.</p>
<h3>Архитектура</h3>
<pre><code>Next.js build
  → OpenNext
    → AWS Lambda (SSR, API routes)
    → S3 (static, assets)
    → CloudFront (CDN)
    → SQS + DynamoDB (ISR revalidation)</code></pre>
<h3>Бұл memory leak-ті неге шешеді</h3>
<p>Lambda функциялары сұрауларды өңдейді және 5-15 минут белсенсіздіктен кейін қайта пайдаланылады. Жад жиналып үлгермейді.</p>
<h3>Деплой</h3>
<pre><code>npx sst@latest init
npx sst deploy --stage production</code></pre>
<h3>Салыстыру</h3>
<table><thead><tr><th></th><th>Vercel</th><th>AWS Lambda (SST)</th><th>Docker self-hosted</th></tr></thead><tbody><tr><td>Memory leak</td><td>Not felt</td><td>Not felt</td><td>Critical</td></tr><tr><td>Cold starts</td><td>Yes</td><td>Yes (~200-500ms)</td><td>No</td></tr><tr><td>Price (~medium traffic)</td><td>$20-150/mo</td><td>$5-30/mo</td><td>$5-50/mo</td></tr><tr><td>Control</td><td>Minimal</td><td>Full</td><td>Full</td></tr><tr><td>ISR/Revalidation</td><td>Works</td><td>Works (SQS)</td><td>Works (with leak)</td></tr><tr><td>Vendor lock-in</td><td>Vercel</td><td>AWS</td><td>None</td></tr></tbody></table>
<h3>Lambda нюанстары</h3>
<ul><li><strong>Cold starts</strong> — бірінші сұрау баяуырақ (~200-500ms)</li><li><strong>Қауіпсіздік</strong> — OAC (Origin Access Control) қос, әйтпесе Lambda URL ашық болады</li><li><strong>OpenNext</strong> — қауымдастық жобасы, ресми Vercel емес. Next.js жаңа мүмкіндіктері бұзылуы мүмкін</li><li><strong>Әмиянға шабуыл</strong> — DDoS кезінде Lambda авто-масштабтауы үлкен шотқа әкелуі мүмкін</li></ul>
<h2>Неге нақты түзету шындыққа жанаспайды</h2>
<h3>1. Архитектуралық мәселе</h3>
<p>Ағу — кездейсоқ қате емес, дизайн шешімінің салдары: Next.js жаһандық <code>fetch</code>-ті ұстап алып, үстіне кэш/трекинг қосады. Оны түзету үшін — App Router-дің fetch-пен қалай әрекеттесетінін қайта жобалау керек. Бұл ISR, revalidation, data cache, request deduplication-ге — фреймворктің өзегіне әсер етеді.</p>
<h3>2. Мүдделер қақтығысы</h3>
<p>Vercel өз платформасына әсер етпейтін нәрсені түзетуге ынталы емес. Self-hosted олардың бизнесімен бәсекелеседі. Self-hosted-те мәселе неғұрлым көп болса — соғұрлым көп адам Vercel-ге көшеді.</p>
<h3>3. Кінәні аудару</h3>
<p>Ресми ұстаным — "бұл undici, біз емес". Ол өзгермейінше — олар түзетумен айналыспайды.</p>
<h3>4. Қауымдастық түзетуі жоқ</h3>
<p>Next.js-тің AGPL-3.0 лицензиясы форктарға рұқсат береді, бірақ код базасы орасан зор әрі Vercel инфрақұрылымымен тығыз байланысқан. fetch орамасын түзететін қауымдастық PR-ы ішкі архитектураны терең түсінуді және мейнтейнерлердің мақұлдауын талап етер еді — ал олар issue-ді әлдеқашан жапты.</p>
<h2>Қорытындылар</h2>
<ol><li><strong>Егер Vercel-де болсаң</strong> — мәселе жоқ, ештеңе істеудің қажеті жоқ</li><li><strong>Егер self-hosted болсаң әрі serverless керек болса</strong> — AWS Lambda-да SST + OpenNext</li><li><strong>Егер self-hosted Docker болса</strong> — мүмкін жерде fetch-ті axios-пен алмастыр, RAM-ды бақыла, под-тарды автоматты қайта іске қосуды баптап қой</li><li><strong>Егер жаңа жоба бастасаң</strong> — осы мәселесіз балама ретінде SvelteKit не Nuxt-ты қарастыр</li></ol>
<h2>Дереккөздер</h2>
<ul><li><a href="https://github.com/vercel/next.js/issues/64212" target="_blank" rel="noopener noreferrer">Issue #64212</a> — Memory Leak with global fetch (April 2024)</li><li><a href="https://github.com/vercel/next.js/issues/68578" target="_blank" rel="noopener noreferrer">Issue #68578</a> — Possible memory leak in Fetch API (August 2024)</li><li><a href="https://github.com/vercel/next.js/issues/85914" target="_blank" rel="noopener noreferrer">Issue #85914</a> — Memory Leak with fetch + standalone (November 2025)</li><li><a href="https://github.com/vercel/next.js/issues/90433" target="_blank" rel="noopener noreferrer">Issue #90433</a> — OOM in 16.0.10 (February 2026)</li><li><a href="https://github.com/vercel/next.js/discussions/88603" target="_blank" rel="noopener noreferrer">Discussion #88603</a> — OOM in Docker/K8s (January 2026)</li><li><a href="https://github.com/vercel/next.js/discussions/88078" target="_blank" rel="noopener noreferrer">Discussion #88078</a> — cacheMaxMemorySize behavior</li><li><a href="https://opennext.js.org/" target="_blank" rel="noopener noreferrer">OpenNext</a></li><li><a href="https://sst.dev/docs/start/aws/nextjs/" target="_blank" rel="noopener noreferrer">SST — Next.js on AWS</a></li><li><a href="https://www.flightcontrol.dev/blog/secret-knowledge-to-self-host-nextjs" target="_blank" rel="noopener noreferrer">Secret knowledge to self-host Next.js</a></li><li><a href="https://nextjs.org/docs/app/guides/memory-usage" target="_blank" rel="noopener noreferrer">Next.js Memory Usage Guide</a></li></ul>]]></content:encoded>
      <pubDate>Thu, 19 Mar 2026 00:00:00 GMT</pubDate>
      <category>Next.js</category>
      <category>Memory Leak</category>
      <category>Docker</category>
      <category>AWS Lambda</category>
      <category>Node.js</category>
    </item>
  </channel>
</rss>