AI coding agenti jsou dobří v infrastrukturní práci, která má dobře známý tvar: Dockerfile pro aplikaci v Node nebo Pythonu, unit systemd, server blok Nginxu před aplikací, CI pipeline, která instaluje, testuje a buildí, databázová migrace a testy kolem toho všeho. Vyrobí to během vteřin a většinou je to blízko správnému řešení. Jsou špatní v detailech, které se mezi verzemi mění - přepínače, klíče konfigurace, výchozí hodnoty - a špatnou věc říkají se stejnou jistotou jako správnou. A když dostanou shell, spouštějí destruktivní příkazy se stejným klidem jako ls. Pravidlo, podle kterého pracujeme, je krátké: agent píše, nástroj validuje, člověk čte každý příkaz a agent nikdy nedrží produkční přihlašovací údaje. Vše níže je to, jak toto pravidlo funguje v praxi.
AI coding agenti jsou dnes běžnou součástí vývoje softwaru a provoz serverů na hostingové platformě jako RE:NODE nám dává názor na to, co se se serverem má a nemá dít. Tento článek jsou ty názory. Nejsou v něm příběhy z bojiště, jen zásady, ke kterým jsme dospěli.
Kde jsou agenti rychlí a přesní#
Agenti si vedou dobře v práci, která byla veřejně napsána desetitisíckrát s drobnými obměnami. Pod to spadá překvapivě velká část DevOps.
- Dockerfily pro běžné stacky. Multi-stage build pro aplikaci v Node, Go, Pythonu nebo .NET, s úzkým runtime image, uživatelem bez rootu a instalací závislostí cachovanou před kopií zdrojáků. Agenti tento vzor znají dobře a pořadí vrstev trefí častěji než unavený člověk.
- Unity systemd.
ExecStart,WorkingDirectory,User,Restart=on-failure,EnvironmentFile, sekce[Install]. Když požádáš o hardening, dostaneš rozumnou saduProtectSystem=,NoNewPrivileges=a spol. - Bloky reverse proxy v Nginxu.
proxy_pass, forwardované hlavičky, hlavičky pro upgrade na WebSocket, přesměrování z HTTP na HTTPS. Běžný případ je solidní. - CI pipeline. Workflow v GitHub Actions, které provede checkout, nastaví runtime, cachuje závislosti, spustí lint a testy a postaví artefakt. Agenti to napíšou rychleji, než někdo najde poslední repozitář, kde se to dělalo.
- Migrace a testy. Při zadaném schématu a změně napíšou migraci a, což je užitečnější, testy, které chování zafixují. Testy jsou místo, kde je nejvíc hodnoty za minutu, protože vygenerovaný test, který selže, je informace tak či tak.
- Pomocné skripty. Pomůcky pro rotaci logů, obaly záloh, skript kontrolující expiraci certifikátu. Krátký Bash a Python s jasnými vstupy a výstupy.
Co mají tyto věci společné: tvar je stabilní po léta, chyby se projeví okamžitě, když to spustíš nebo zvaliduješ, a cena špatného prvního návrhu je minuta kontroly. To je pásmo, kde je agent jasná výhra, a zpátky k ručnímu psaní už bychom nechtěli.
Existuje i druhá, tišší výhra: čtení. Agent, který smí číst repozitář, adresář s konfigurací a pár logů a pak vysvětlit, co se děje, je často rychlejší než grepování v systému, na který se naposledy sáhlo před měsíci. Čtení nemůže nic rozbít. Víc o tom níž.
Kde agenti sebejistě chybují#
Selhání není v tom, že agenti produkují nesmysly. Je v tom, že produkují věrohodnou konfiguraci, která byla někdy správná, pro nějakou verzi, na nějaké distribuci - a nic ve výstupu neřekne, která část je zastaralá.
Vzorce, na které si dáváme pozor:
- Vymyšlené přepínače. Přepínač CLI, který zní, jako by měl existovat, ve stylu pojmenování nástroje, a neexistuje. Vypadá přesně jako skutečné přepínače kolem něj.
- Zastaralé klíče. Konfigurace, která byla správná před několika hlavními verzemi. Model viděl mnohem víc staré dokumentace než nové, takže staré podoby jsou zastoupeny nadměrně.
- Smíchané verze. Soubor, kde polovina direktiv pochází z jedné hlavní verze a polovina z jiné. Každý řádek vypadá dobře; kombinace nikdy neexistovala.
- Rozmazání distribucí. Cesty a názvy balíčků z Debianu v souboru určeném pro stroj z rodiny RHEL, nebo naopak.
- Tiché výchozí hodnoty. Volba nastavená výslovně na to, co agent považuje za výchozí, i když se výchozí hodnota změnila, nebo důležitá volba vynechaná, protože agent předpokládal výchozí hodnotu, která tam není.
Několik skutečných příkladů driftu, které agenti reprodukují a které si můžeš ověřit v upstream dokumentaci:
| Co agent napíše | Aktuální podoba | Proč na tom záleží |
|---|---|---|
listen 443 ssl http2; | listen 443 ssl; plus http2 on; | Nginx 1.25.1 parametr označil jako zastaralý |
ssl on; | ssl na řádku listen | Zastaralé od Nginx 1.15.0, později odstraněno |
version: "3.8" v Compose | vynechat | Compose to ignoruje a varuje |
MemoryLimit= v unitu | MemoryMax= | Starý název je z éry cgroup v1 |
apt-key add | keyring v signed-by= | apt-key je zastaralý |
echo "::set-output ..." | zapsat do $GITHUB_OUTPUT | GitHub příkaz označil jako zastaralý |
Nic z toho není exotické. V tom je právě podstata. Špatná podoba je ta, kterou většina internetu používala léta, takže je ta, po které model sáhne první. Něco z toho pořád funguje s varováním, což je horší než selhání: soubor skončí v repozitáři, varování v logu, který nikdo nečte, a další upgrade starou podobu odstraní.
Jistota agenta nevypovídá nic o jeho správnosti. Špatný klíč konfigurace i správný přijdou ve stejně klidném tónu.
Nepříjemné je, že agent bude špatnou odpověď i hájit, když se zeptáš „opravdu to tak je?“. Někdy se opraví, někdy trvá na svém a někdy se omluví a změní správný řádek na špatný. Zeptat se modelu není ověření. Ověření je spustit kontrolní nástroj samotného programu.
Destruktivní příkazy a proč je nebezpečný právě shell#
Zápis souboru se vrací snadno. Spuštění příkazu často ne. Skok od „agent upravuje soubory v repozitáři“ k „agent spouští příkazy na stroji“ je místo, kde je skutečné riziko, a je to krok, který lidé dělají nejlehkovážněji.
Příkazy, které bereme jako nikdy-bez-člověka, ať je navrhuje kdokoli:
rm -rfs proměnnou v cestě. Pokud je proměnná prázdná,rm -rf "$DIR/"se změní narm -rf /. Agenti píší tento vzor, protože ho píší lidé.docker system prune -a --volumesnebodocker volume rm. Ve volumes žijí databáze. „Uvolni místo na disku“ je naprosto rozumná žádost, která končí prázdnou databází.docker compose down -v. Přepínač-vodstraní pojmenované volumes. Vypadá jako úhledný přepínač.git reset --hard,git clean -fdx,git push --force. Na pracovní větvi v pořádku, na sdílené nebo na serveru s necommitnutou lokální konfigurací zhoubné.DROP,TRUNCATE,DELETEbezWHEREnebo migrace, která zahodí sloupec ve stejném vydání, které do něj přestane zapisovat.chown -Rachmod -Rze špatného adresáře.- Změny firewallu přes SSH.
ufw enablepředufw allow OpenSSHneboiptables -Fna řetězci s výchozí politikouDROPa relace, ve které píšeš, je pryč. terraform applyneboansible-playbookbez předchozího pohledu na plán nebo kontrolní běh.
Řekněme, že agent má opravit „plný disk“ na stroji ve 3:00 ráno. Nejrychlejší cesta k volnému místu je smazat největší věc a největší věc na serveru je velmi často data, za jejichž uchování se platí. Člověk by tu zaváhal. Agent optimalizující na „úkol splněn“ ne, pokud to nezařídíš tak, aby nemohl.
Případ firewallu si zaslouží vlastní poznámku, protože neničí data, ničí přístup. Pokud agenta k firewallu vůbec pouštíš, bezpečné pořadí je povolit nejdřív SSH a při zapínání mít otevřenou druhou relaci - průvodce UFW tu posloupnost popisuje.
Ochrana: nejdřív jen čtení#
Nejužitečnější ochrana nestojí nic: každou infrastrukturní relaci začni tím, že agent čte, ne dělá.
Čtení znamená prohlížet konfigurační soubory, vypisovat služby, číst logy, spouštět příkazy pro stav. Na serveru jde o věci jako systemctl status, journalctl -u app --since "1 hour ago", ss -tlnp, df -h, docker ps, nginx -T. Žádná z nich nemění stav. Nech agenta udělat si obrázek a navrhnout změnu jako diff nebo seznam příkazů, a teprve pak rozhoduj.
Claude Code má plan mode, kde čte a navrhuje bez úprav, a Claude Code i Cursor lze nastavit tak, aby se před spuštěním terminálových příkazů ptaly. Nech to zapnuté u všeho, co se dotýká infrastruktury. V Claude Code můžeš jít dál s pravidly oprávnění v settings.json: povolit příkazy jen pro čtení, aby se nevyptávaly, a zakázat ty, které nikdy nemají běžet bez dozoru:
{ "permissions": { "allow": [ "Bash(systemctl status:*)", "Bash(journalctl:*)", "Bash(docker ps:*)", "Bash(nginx -t)", "Bash(git diff:*)" ], "deny": [ "Bash(rm -rf:*)", "Bash(docker system prune:*)", "Bash(git push --force:*)", "Read(./.env)", "Read(./secrets/**)" ] }}Seznam zákazů je bezpečnostní pás, ne zeď. Příkazy se dají formulovat mnoha způsoby a odhodlaný řetěz shellu dokáže obejít shodu na prefix. Ber tato pravidla jako způsob, jak zastavit běžnou nehodu, a skutečnou ochranu nech tam, kam patří: v tom, k čemu se prostředí agenta vůbec dostane.
Ochrana: dry runy, kontrola a staging kopie#
Po režimu jen pro čtení je další vrstva taková, že každá změna ukáže, co by udělala, než to udělá. Většina vážných infrastrukturních nástrojů má na to režim a agentovi je třeba říct, ať ho používá pokaždé.
$ terraform plan -out=change.plan$ ansible-playbook site.yml --check --diff$ rsync -av --delete --dry-run ./site/ deploy@host:/var/www/site/$ kubectl apply -f deploy.yaml --dry-run=server$ kubectl diff -f deploy.yamlPak čti každý příkaz, než se spustí. Každý. Zní to pomalu a není, protože většina příkazů v relaci jsou čtení a přečíst jednořádkový příkaz zabere vteřiny. Ty, které mění stav, jsou ty, které těch pár vteřin stojí za to. Ve chvíli, kdy zaznamenáš, že schvaluješ bez čtení, přepni agenta zpět na žádání o plán.
Kontroluj diff, ne shrnutí. Agenti píšou dobrá shrnutí toho, co změnili, a shrnutí někdy neodpovídá diffu. „Upravena konfigurace Nginxu, přidáno cachování“ může přijít s odstraněným blokem location, o kterém nepadlo ani slovo. git diff je zdroj pravdy.
Poslední vrstva je místo, kde agent pracuje. Agenti mají pracovat nad lokálním checkoutem, kontejnerem nebo staging kopií - nikdy přímo nad produkcí. Staging kopie je druhé prostředí stejného tvaru jako produkce, s vlastními přihlašovacími údaji a vlastními daty, kde špatný příkaz stojí přestavbu, ne omluvu. Praktickou stranu popisují články staging a produkce na jednom účtu a pro herní servery testovací server vedle produkce. Agent tam může být tak rychlý, jak chce. Povýšení do produkce je samostatný, nudný krok kontrolovaný člověkem: merge a deploy pipeline, ne agent s SSH.
Nikdy nedávej agentovi produkční přihlašovací údaje#
Toto je pravidlo, které zůstane, i kdyby zbylo jen jedno.
Relace agenta čte spoustu textu, který jsi nenapsal: zdrojáky závislostí, soubory README, komentáře u issues, webové stránky, řádky logů, chybové hlášky cizích služeb. Kterýkoli z těch textů může obsahovat instrukce a současné modely lze ovlivnit instrukcemi ukrytými v obsahu, který čtou. To je prompt injection a nikdo nemá kompletní řešení. Spoj to s přihlašovacími údaji a shellem a otázka nezní, jestli má agent dobré úmysly, ale jestli je mají všechny texty, které čte.
Praktická pravidla:
- Žádné produkční databázové URL, cloudové klíče, SSH klíče k produkčním hostům ani deploy tokeny v prostředí agenta. Ani v souborech
.env, které může číst, ani v historii shellu, ani vložené do chatu „jen tentokrát“. - Agent pracuje s údaji pro staging, omezenými na staging, jejichž únik by nijak zvlášť neublížil.
- Deploy do produkce probíhá přes CI s vlastními secrets, spuštěné mergem, který člověk schválil. Agent může napsat pipeline. Nespouští ji.
- Pokud se agent potřebuje podívat na produkci, čte exportované logy nebo metriky, nebo člověk spustí příkaz jen pro čtení a vloží výstup.
Tam, kde servery řídíš přes panel místo čistého SSH, platí nejmenší oprávnění stejně: dej každému člověku i nástroji jen přístup, který potřebuje, na dobu, kterou potřebuje. Myšlenka je rozepsána v článku subuživatelé a nejmenší oprávnění a nakládání se secrets samotnými v článku proměnné prostředí a secrets.
Jak ověřit vygenerovanou konfiguraci#
Každý formát konfigurace, který stojí za použití, má kontrolní nástroj a důležitý je návyk spustit ho dřív, než se cokoli znovu načte. Řekni agentovi, ať validátor spustí jako součást změny, a jeho výstup čti sám.
| Soubor | Validace | Zachytí |
|---|---|---|
| Konfigurace Nginxu | nginx -t | Syntaxi, neznámé direktivy |
| Unit systemd | systemd-analyze verify app.service | Neznámé klíče, špatné cesty |
| Dockerfile | hadolint Dockerfile | Špatné postupy, pinning |
| Soubor Compose | docker compose config | Schéma, interpolaci |
| GitHub Actions | actionlint | Výrazy, zastaralou syntaxi |
| Shellový skript | shellcheck script.sh | Uvozování, prázdné proměnné |
| Konfigurace sshd | sshd -t | Syntaxi dřív, než se zamkneš venku |
| sudoers | visudo -c | Rozbitý soubor, který zničí sudo |
Validace zachytí syntaxi a neznámé klíče. Nezachytí konfiguraci, která je platná a špatná - proxy_pass na špatný port, unit, který startuje jako root, protože chybí User=, cache, která ukládá odpovědi s cookies. U toho ručně zkontroluj tři věci:
- Každý přepínač a klíč, který nepoznáváš, ověř v upstream dokumentaci pro skutečně nainstalovanou verzi. Nejdřív
nginx -v,docker --version,systemctl --version, pak dokumentace k té verzi. Tady vymyšlené přepínače umírají. - Co dělá při selhání. Co se stane, když aplikace spadne, upstream je mimo, disk se zaplní. Unit s
Restart=alwaysa bez limitů restartuje rozbitou aplikaci donekonečna; to může, ale nemusí být to, co chceš. - Kdo ji spouští a čeho se může dotknout. Uživatel, oprávnění souborů, vystavené porty, připojené volumes. Agenti míří spíš k tomu, aby věc fungovala, než aby byla zamčená, takže bezpečnostní postoj nastavuje člověk.
Pak změna jde na staging a zkouší se: služba se čistě restartuje, health check projde, logy jsou tiché. Teprve potom jde stejný soubor do produkce běžnou cestou. U konkrétních formátů je s čím vygenerovaný výstup porovnat průvodce systemd, průvodce reverse proxy v Nginxu a Docker Compose pro malé stacky.
Migrace, CI a testy si zaslouží zvláštní podezřívavost#
Tyto tři vypadají jako obyčejný kód, takže dostávají obyčejnou kontrolu. Měly by dostat víc.
Migrace je kód, který běží jednou nad jedinou kopií dat. Agent ochotně napíše migraci, která přejmenuje sloupec, což je z pohledu běžící aplikace na živém systému zahození a přidání. Napíše backfill, který zamkne velkou tabulku. Napíše down migraci, která nedokáže obnovit to, co up odstranilo. Naše pravidlo: každá vygenerovaná migrace běží nad obnovenou kopií skutečně velkých dat, než se dostane k produkci, a destruktivní změny se rozdělují do více vydání - přestat sloupec používat, nasadit, pak ho odstranit. Migrace bez výpadku popisují vzor expand-and-contract a stojí za to přimět agenta, aby ho výslovně dodržel, protože sám od sebe ne. A obnovená kopie existuje jen tehdy, když se zálohy dají obnovit, což je samostatná disciplína: zálohy, které opravdu fungují.
CI pipeline běží se secrets. Vygenerovaný workflow, který používá pull_request_target, checkoutuje kód pull requestu a spouští ho, předal secrets repozitáře každému, kdo otevře pull request. Workflow, který připíná akce třetích stran na pohyblivý tag místo na commit, důvěřuje tomu, kdo ten tag ovládá. Agenti produkují oba vzory, protože oba jsou běžné ve veřejných repozitářích. Přečti si triggery a blok oprávnění u každého vygenerovaného workflow a nastav permissions: výslovně na minimum.
Testy jsou ta nenápadná věc. Agent, který má zařídit, aby testy prošly, má dva způsoby: opravit kód, nebo změnit test. Někdy zvolí ten druhý, potichu - zmírní assertion, přidá skip, namockuje právě to, co se testuje. Zelená sada testů od agenta není důkaz, dokud se nepodíváš, co se v testech změnilo. Při kontrole diffu od agenta se změny testů čtou první.
Jak funguje smyčka s AI#
Když jsou agenti hlavní vývojová smyčka, ne asistent, práce člověka se mění. Píše se méně a kontroluje víc a kontrola je ta skutečná práce. Zásady, kterými se smyčka řídí:
- Malé, čitelné změny. Jedna věc na změnu. Agent ochotně refaktoruje deploy skript, Dockerfile a CI pipeline najednou; tři diffy, z nichž každý jde přečíst za pár minut, jsou lepší.
- Tvrzení agenta nejsou důkaz. „Je to otestované“ neznamená nic, dokud neuvidíš příkaz a jeho výstup. „Tenhle přepínač existuje“ neznamená nic, dokud neuvidíš dokumentaci.
- Validátory a testy ve smyčce, ne po ní. Agent spouští
nginx -t,shellcheck, sadu testů a selhání čte sám. Tím se zachytí většina driftu, než diff vůbec uvidí člověk. - Změny infrastruktury jdou stejnou pipeline jako kód. Žádné hotfixy na serverech spuštěné agentem. Co stojí za změnu, stojí za commit.
- Merge a produkce patří lidem. Agent může navrhnout cokoli. Nemůže nic vydat.
- Pravidla projektu pro agenta jsou napsaná. Krátký soubor s instrukcemi v repozitáři - které příkazy jsou bezpečné, jaká prostředí existují, čeho se nikdy nedotýkat - zabere víc než připomínání agentovi v každé relaci.
Přínos je skutečný. Infrastrukturní boilerplate, který dřív zabral odpoledne, jsou dnes minuty kontroly a ten čas jde do částí, které potřebují úsudek: jaké jsou režimy selhání, kde žijí data, kdo má přístup. Agent zrychluje lehkou část. Těžká část nezmizí a předstírat opak je cesta, jak se z rychlé smyčky stane rychlý způsob, jak věci rozbít.
Co agentovi nesvěřujeme#
Některou práci necháváme lidem, ne proto, že by agenti nedokázali něco vyprodukovat, ale proto, že cena věrohodné špatné odpovědi je příliš vysoká a příliš těžko rozpoznatelná:
- Reakce na incident na živém systému. Agent pod tlakem „oprav to hned“ je přesně prostředí pro destruktivní zkratku. Může číst logy a navrhovat příčiny; příkazy píše člověk.
- Cokoli nevratného nad skutečnými daty. Obnovy přes živou databázi, mazání, změny retence. Takových věcí je málo, takže zrychlení je malé a riziko neomezené.
- Návrh řízení přístupu. Kdo se k čemu dostane, politika firewallu, role IAM. Agenti píší syntaxi dobře a politiku špatně, většinou chybují směrem k tomu, co odstraní chybu.
- Upgrady jádra infrastruktury na nové verze. Hlavní verze databází, změny kernelu a initu. Znalosti agenta jsou nejslabší přesně tam, kde jsou release notes nejnovější.
Všechno ostatní je v pořádku, pokud platí výše uvedená ochranná opatření.
FAQ#
Je pro DevOps lepší Claude Code, nebo Cursor?
Oba zvládají stejné základní úkoly a důležitější než editor je model za nimi. Pro infrastrukturu záleží na kontrole nad spouštěním příkazů: schválení před spuštěním, pravidla povolení a zákazu, plánovací režim jen pro čtení. Vyber ten, jehož kontroly opravdu necháš zapnuté.
Můžu agentovi dovolit přihlásit se na server přes SSH?
Na odhazovací staging stroj ano, s příkazy schvalovanými po jednom. Na produkci ne. Změna v produkci má být zkontrolovaný commit nasazený pipeline, a pokud agent potřebuje informace z produkce, člověk může spustit příkaz jen pro čtení a sdílet výstup.
Jak zabránit agentovi, aby si vymýšlel volby konfigurace?
Zabránit nejde, jen to zachytit. Řekni agentovi přesné nainstalované verze, nech ho po každé změně spustit validátor nástroje a každý klíč nebo přepínač, který nepoznáváš, ověř v dokumentaci pro danou verzi. Hodně pomůže i vložení příslušné části aktuální dokumentace do relace.
Jsou AI generované Dockerfily bezpečné?
Běžné vzory obvykle jsou, po kontrole. Ověř, že základní image je připnutý na skutečný aktuální tag, že poslední stage běží jako uživatel bez rootu, že se do něj nekopírují secrets ani nepředávají jako build argumenty, a spusť na něm hadolint. Pak ho sestav a spusť na stagingu jako každý jiný image.
Má agent psát i testy, nebo jen kód?
Ano, i testy, je to jedna z jeho nejužitečnějších činností. Ale v každém diffu čti změny testů dřív než změny kódu, protože agent, který chce dostat zelenou, může zmírnit assertion nebo přeskočit test místo opravy chyby.


