# Dev, staging și prod din prima zi: de ce o echipă de trei n-ar trebui să aștepte

„Separăm mediile mai târziu, când avem utilizatori.” Am auzit propoziția asta de la fiecare echipă mică cu care am lucrat și am spus-o și noi. Sună a prudență: nu construi infrastructură de care n-ai nevoie încă. E de fapt un împrumut, luat la cea mai proastă dobândă posibilă, iar articolul ăsta e graficul de rambursare.

Am [scris ghidul](/ro/blog/aws-three-accounts-one-cdk-codebase) pentru rularea a trei conturi AWS dintr-un singur cod CDK. Ăsta e de ce-ul, adresat echipei care are un cont cu totul în el și un motiv bun să-l țină așa încă un trimestru.

## Cât costă „mai târziu”

Separarea mediilor în prima zi e o zi de muncă. Separarea lor la luna șase e o săptămână. Separarea lor la luna optsprezece e o lună, plus un risc pe care nu-l poți evalua. Costul nu crește liniar; crește cu numărul de lucruri care au acumulat stare și nume.

<div class="article-figure">
<svg viewBox="0 0 900 260" width="100%" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="Grafic al efortului de a separa mediile în funcție de vârsta proiectului. În prima zi e cam o zi. La luna șase e cam o săptămână, pentru că baza de date, secretele și DNS-ul au date și nume reale. La luna optsprezece e cam o lună, cu o bandă de risc hașurată, pentru că datele de producție trebuie mutate din contul comun fără downtime și fiecare integrare arată spre contul vechi. O a doua linie plată arată costul de rulare a trei medii de la început: cam 60 de dolari pe lună.">
<g font-family="Inter,system-ui,sans-serif" font-size="12">
<text x="20" y="24" fill="#f1f3ff" font-size="14" font-weight="700">Efortul de a separa mediile vs. vârsta proiectului</text>
<line x1="70" y1="220" x2="870" y2="220" stroke="#9aa3c7"/><line x1="70" y1="40" x2="70" y2="220" stroke="#9aa3c7"/>
<text x="70" y="240" text-anchor="middle" fill="#9aa3c7" font-size="10">ziua 1</text><text x="370" y="240" text-anchor="middle" fill="#9aa3c7" font-size="10">luna 6</text><text x="770" y="240" text-anchor="middle" fill="#9aa3c7" font-size="10">luna 18</text>
<text x="64" y="216" text-anchor="end" fill="#9aa3c7" font-size="10">1 zi</text><text x="64" y="150" text-anchor="end" fill="#9aa3c7" font-size="10">1 săpt.</text><text x="64" y="60" text-anchor="end" fill="#9aa3c7" font-size="10">1 lună</text>
<path d="M70,214 C200,212 300,180 370,150 C500,110 650,80 770,56" fill="none" stroke="#ff6b8a" stroke-width="2.5"/>
<path d="M600,90 C700,70 770,56 800,50 L800,120 C740,110 650,120 600,130 Z" fill="#ff6b8a" opacity="0.12"/>
<text x="700" y="140" text-anchor="middle" fill="#ff6b8a" font-size="10">+ risc: mutarea datelor de prod dintr-un cont comun</text>
<circle cx="70" cy="214" r="5" fill="#4fffb0"/><text x="84" y="206" fill="#4fffb0" font-size="11">3 conturi, 1 map de config, 1 rol OIDC fiecare</text>
<circle cx="370" cy="150" r="5" fill="#ffd166"/><text x="384" y="146" fill="#ffd166" font-size="11">baza de date, secretele, DNS-ul au nume și date</text>
<circle cx="770" cy="56" r="5" fill="#ff6b8a"/><text x="760" y="46" text-anchor="end" fill="#ff6b8a" font-size="11">fiecare integrare arată spre contul vechi</text>
<line x1="70" y1="200" x2="870" y2="200" stroke="#4fffb0" stroke-width="1.5" stroke-dasharray="5,4"/><text x="868" y="196" text-anchor="end" fill="#4fffb0" font-size="10">costul de rulare a 3 medii din ziua 1: ~$60 / lună</text>
</g>
</svg>
</div>

La luna șase baza de date are date reale și un nume real. Secretele au fost copiate în trei locuri. DNS-ul arată spre lucruri. Laptopul cuiva are un profil numit `default` care face deploy în singurul cont care există. Mutarea producției din contul ăla înseamnă [importarea resurselor cu stare în stack-uri noi](/ro/blog/cloudformation-500-resource-limit-split-a-stack), reîndreptarea fiecărui webhook și a fiecărei integrări, rotirea fiecărui secret care a fost partajat, și toate astea fără downtime. Am făcut-o. E un proiect cu un runbook, iar runbook-ul are o secțiune de rollback, și ai prefera să n-ai nevoie de ea.

## Cele trei lucruri care merg prost într-un singur cont

Nu argumentăm din teorie. Fiecare dintre astea s-a întâmplat unei echipe cu care am lucrat, iar una ni s-a întâmplat nouă.

**Date de test în producție, sau date de producție în test.** Cu un singur cont, „baza de date” e un singur cluster, iar separarea e un nume de schemă sau un prefix de tabelă. Un script de migrare cu `search_path` greșit, o comandă de seed rulată în shell-ul greșit, un query de analiză îndreptat spre tabela greșită: fiecare e o greșeală de un caracter și fiecare s-a întâmplat. În conturi separate, greșeala de cont e tot posibilă, dar cere asumarea unui alt rol, iar tooling-ul de deploy poate [refuza să facă asta fără un flag explicit](/ro/blog/same-tag-deploy-and-the-deploy-script-without-ci).

**Un deploy care era pentru dev.** [Incidentul nostru](/ro/blog/cloudformation-deleted-our-app-runner-services): un `cdk deploy` de pe un laptop, cu contextul greșit, a șters fiecare serviciu App Runner dintr-un mediu. Era mediul de dev, în contul de dev, iar paguba totală a fost o după-amiază. Aceeași comandă, într-o configurație cu un singur cont, șterge producția. Granița contului n-a prevenit greșeala; a limitat-o. Asta e toată valoarea.

**Hotfix-ul „doar de data asta”.** Un singur cont fără staging înseamnă că singurul loc unde poți testa un fix e producția. Așa că fix-ul merge în producție, și merge, și următorul la fel, și în cele din urmă procesul de deploy al echipei devine „push pe main și ne uităm”. Obiceiul nu e prostie. E răspunsul rațional la a nu avea unde altundeva să te uiți. Dă-le oamenilor un cont de staging care costă $20 pe lună și îl folosesc, pentru că există.

## Ce înseamnă de fapt „din prima zi”

Obiecția la separarea timpurie e de obicei o imagine a unui landing zone: Control Tower, o organizație cu o duzină de OU-uri, service control policies, un cont de shared services, logging centralizat, un cont de rețea cu Transit Gateway. Asta e o lună de muncă și e lucrul corect pentru o companie cu cincizeci de ingineri. E lucrul greșit pentru trei, și nu e ce propunem.

Minimul care captează aproape toată valoarea:

| Fă în prima zi | Sari până ai nevoie |
|---|---|
| O organizație AWS cu trei conturi membre: dev, staging, prod | Control Tower, acceleratoare de landing zone |
| Un map `config` în codul CDK, indexat pe mediu | Unități organizaționale dincolo de cea implicită |
| Un [rol de deploy OIDC per cont](/ro/blog/github-oidc-deploy-roles-per-aws-account), restrâns pe branch | Service control policies |
| `RemovalPolicy.RETAIN` și deletion protection în prod | Conturi de shared services sau de rețea |
| O vedere de facturare consolidată, tagată pe mediu | CloudTrail centralizat într-un cont de securitate |
| Un script de deploy care refuză prod fără `--env prod --yes` | VPC peering între conturi |

Asta e o zi. Diff-ul CDK de la un mediu la trei e map-ul de config și valoarea de context `env`; tot restul din stack e deja parametrizat sau ar trebui să fie. Iar cele trei conturi costă, la dimensiune minimă, cu Aurora pe pauză și App Runner la 0,25 vCPU, [cam $60 pe lună mai mult](/ro/blog/aws-bill-of-a-three-person-startup) decât unul. Asta e toată prima, și e mai puțin decât o oră a inginerului care altfel ar face migrarea de la luna optsprezece.

## Ce primești în ziua a doua

Separarea nu e doar o proprietate de siguranță. E ce face posibile alte câteva lucruri bune, iar alea sunt cele care o plătesc:

- **[Mediile de preview per pull request](/ro/blog/preview-environments-per-pull-request-on-aws)** au nevoie de un loc unde oricine poate face deploy la orice fără să ceară voie. Ăla e contul de dev, cu un trust policy OIDC permisiv care ar fi inacceptabil în prod.
- **Un deploy de producție aprobat de un om** are nevoie de un environment GitHub cu revieweri și de un rol pe care doar acel environment îl poate asuma. Ăla e trust policy-ul contului de prod.
- **Urmărirea onestă a costurilor.** „Cât costă producția?” e un filtru pe ID de cont, nu un proiect de arheologie prin tag-uri.
- **Rază de explozie pentru credențiale.** O cheie de dev scursă nu poate atinge datele de prod, pentru că nu e în contul ăla. Am [șters oricum cheile cu viață lungă](/ro/blog/github-oidc-deploy-roles-per-aws-account), dar granița e stratul de dedesubt.
- **Hardening diferit per mediu fără ramificare în cod.** DESTROY în dev, RETAIN în prod, point-in-time recovery pornit în staging și prod, dintr-un singur tabel dintr-un singur fișier.

Niciunul nu e posibil într-un singur cont fără să construiești izolarea în interiorul lui de mână, cu politici IAM mai greu de nimerit decât granița de cont pe care o imită.

## Cazul cu un singur cont care chiar e în regulă

Dacă construiești un prototip care va fi aruncat în opt săptămâni, un cont e în regulă, iar separarea lui e risipă. Testul e dacă există o bază de date cu date pe care ți-ar părea rău să le pierzi. Ziua în care răspunsul e da e ziua în care ai trecut de prototip, și de obicei e mai devreme decât crede echipa: prima dată când se înscrie un utilizator real, prima dată când se generează o factură, prima dată când cineva zice „nu rula aia pe cea live”.

Fă-o în ziua aia, nu în ziua incidentului.

Dacă ai un cont cu totul în el și vrei configurația din prima zi retrofitată înainte să devină proiectul de la luna optsprezece, [hai să vorbim](/contact). Le-am făcut pe amândouă, iar prima e mult mai ieftină.
