Folosim Claude Code zilnic pentru muncă de infrastructură, pe o platformă pe care o operăm pentru un client din SUA: stack-uri CDK, scripturi de deploy, politici IAM, alarme, runbook-uri. Cam o treime din codul de infrastructură comis în ultimele șase luni a fost schițat mai întâi de el. Asta e o contabilitate sinceră: ce a scris și am făcut merge ca atare, ce a scris și am corectat, și cele patru propuneri pe care le-am refuzat, cu motivele, pentru că refuzurile sunt mai instructive decât reușitele.
Forma muncii
Schimbările de infrastructură pe platforma asta trec prin aceeași revizuire de pull request ca și codul de aplicație, și output-ul agentului trece și el prin ea. Rulează într-un sandbox cu acces de citire la repository și un rol AWS limitat care poate face cdk diff pe staging și poate citi CloudWatch, dar nu poate face deploy. Fiecare schimbare pe care o propune e un branch și un PR. Un om citește diff-ul, citește output-ul cdk diff pe care agentul îl lipește în PR, și face merge sau nu. Deploy-urile sunt același script dintotdeauna, rulat de un om.
Configurația asta e motivul pentru care restul articolului e calm. Agentul poate propune orice; nu poate executa nimic. Fiecare dintre refuzurile de mai jos a fost prins la PR.
Merge ca atare
Alarme și dashboard-uri CloudWatch. Cea mai bună categorie. Dat „alarmă pe DLQ-ul de email, rata de 5xx din App Runner și Aurora ACU la maxim timp de 10 minute”, a produs CDK corect cu praguri rezonabile, statisticile potrivite (p99, nu medie, pentru latență), treatMissingData setat cum trebuie și un topic SNS legat la canalul de on-call existent. Alarmele sunt plictisitoare, bine documentate și au un răspuns corect evident; ăsta e punctul dulce.
Scriptul exercițiului de restaurare. Scriptul care reaplică PITR, TTL, stream-uri și tag-uri pe o tabelă DynamoDB restaurată, citind setările dorite din definițiile CDK. Vreo 120 de linii de TypeScript cu AWS SDK, corect din primul PR, inclusiv paginarea pe care oamenii o uită.
Runbook-uri. Dat extrasul de log al unui incident și soluția aplicată, scrie o pagină de runbook clară în formatul casei. Runbook-urile lui sunt mai bune decât ale noastre, pentru că nu sare peste pașii pe care îi consideră evidenți.
Politica de trust a rolului de deploy OIDC. Condiție sub corectă, audiență corectă, limitată la repository și branch. A adăugat și, necerut, un comentariu care explică de ce claim-ul sub trebuie potrivit cu StringLike pentru tiparul de branch, care e lucrul pe care oamenii îl greșesc.
Propagarea tag-urilor de cost. O schimbare transversală care adaugă tag-urile CostCenter și Owner la fiecare stack printr-un aspect. Mecanică, a atins 40 de fișiere, zero greșeli.
Merge după corecturi
Stack-ul de VPC endpoint-uri. A adăugat endpoint-urile corect, dar a pus endpoint-ul S3 ca endpoint de interfață, nu de gateway, ceea ce funcționează, dar costă 7 $ pe lună pentru ceva gratuit. Prins citind cdk diff. Genul de greșeală pe care ar face-o și un om care n-a mai făcut asta.
O schimbare de concurență la un Lambda. A setat concurență rezervată pe o funcție ca să protejeze un API din aval, ceea ce era cerința, dar a ales numărul citind rate limit-ul documentat al aval-ului și împărțind la durata medie a unei invocări. Aritmetica era corectă; presupunerea că invocările sunt distribuite uniform nu era. L-am înjumătățit. Raționamentul era în descrierea PR-ului, ceea ce a făcut corectura o conversație de un minut.
Strângerea security group-urilor. Cerut să reducă ingress-ul security group-ului Aurora doar la conectorul VPC al App Runner și la Lambda-uri, a făcut-o, și a scos și regula pentru bastion host-ul la care runbook-ul încă face referire pentru acces de urgență. Bastionul e oprit 99 % din timp, deci cdk diff în staging n-a arătat nimic stricat. Un om care știa de bastion a pus regula la loc. Agentul n-avea cum să știe, pentru că rolul bastionului era în capul cuiva, nu în repository. Acum e în repository.
Refuzate
1. „Adaugă cdk deploy la rolul agentului ca să pot verifica schimbările cap-coadă.” Propus într-o descriere de PR ca îmbunătățire de eficiență, cu un argument bine construit: diff nu e același lucru cu deploy, unele erori apar doar la deploy, staging-ul e izolat. Toate adevărate. Refuzat, pentru că tot modelul se bazează pe faptul că agentul nu poate schimba starea AWS, iar „doar staging” e felul în care asta se erodează. Staging-ul împarte codebase-ul CDK cu producția; un bug de deploy în staging e o previzualizare a unuia în producție, și vrem ca un om să vadă previzualizarea.
2. Un RemovalPolicy.DESTROY pe clusterul Aurora de staging „ca să facă demontarea mai rapidă”. Corect izolat; staging-ul e de unică folosință. Refuzat pentru că același construct e instanțiat pentru producție cu un flag de mediu, și am pierdut resurse din exact forma asta de schimbare. Regula în codebase-ul ăsta e că politicile de eliminare sunt RETAIN peste tot și demontarea e o operațiune manuală, numită. Agentul n-avea cum să știe istoria; CLAUDE.md o spune acum.
3. O instrucțiune IAM cu wildcard ca „să deblocheze indexatorul de căutare”. Indexatorul pica cu access denied pe un ARN nou de stream DynamoDB. Soluția propusă era dynamodb:* pe *, cu un comentariu „de strâns mai târziu”. Refuzat, evident, iar partea interesantă e că, întrebat pentru soluția minimă în schimb, a produs ARN-ul exact al stream-ului și cele patru acțiuni de care are nevoie consumatorul în mai puțin de un minut. Wildcard-ul nu era o limită de capabilitate; era calea minimei rezistențe, și revizuirea e ce a făcut cealaltă cale să fie luată.
4. Mutarea secretelor din Secrets Manager în SSM pentru a economisi 8 $ pe lună. O economie reală, calculată corect, și un argument pe care l-am făcut și noi. Refuzat pentru cele două secrete în cauză, pentru că sunt rotite de Lambda-ul de rotație al Secrets Manager, pe care SSM nu-l are, iar PR-ul nu ținea cont de rotație. Jumătate a primit merge: cele trei secrete statice s-au mutat, cele două rotite au rămas.
Ce spune tiparul
Merge-urile sunt muncă mecanică, bine documentată, cu un singur răspuns corect. Corecturile sunt locuri în care răspunsul corect depindea de ceva care nu era în repository: bastionul, forma traficului. Refuzurile sunt toate despre raza de impact sau istorie, și niciunul nu e despre capabilitate: în fiecare caz agentul putea produce versiunea sigură în momentul în care i s-a cerut.
Deci munca nu e „poate agentul să scrie cod de infrastructură”. Poate. Munca e să faci repository-ul să conțină lucrurile care trăiau în capete: regula politicii de eliminare, scopul bastionului, de ce cele două secrete se rotesc. Fiecare refuz de mai sus a devenit o linie în CLAUDE.md sau un comentariu în construct, și aceeași propunere n-a mai revenit.
Iar granița sandbox-ului nu e negociabilă, oricât de bun ar fi argumentul de eficiență. Agentul schițează; omul face deploy. Ăsta e tot motivul pentru care o treime din infrastructură poate fi schițată de agent fără ca cineva să-și piardă somnul.
Dacă vrei să folosești un agent pe infrastructura ta și încerci să decizi unde e linia, noi am tras-o o dată și te ajutăm s-o tragi pe a ta.