Blog ·
O rulare care își tipărea propriul eșec
Rularea de verificare scria fiecare cod de ieșire într-un rezumat, apoi scria DONE oricum. O lansare a plecat peste o linie roșie necitită.
De Provibr
Înaintea unei lansări rulăm un singur script. El execută suita de teste a webului, suitele de pachete, de gateway și de unelte, zece verificări de tipuri, lintul, verificarea de conținut, lintul de protocol și generarea de cod a protocolului, și scrie câte o linie pe pas într-un fișier de rezumat: numele pasului și codul lui de ieșire. Apoi scrie DONE.
Scria DONE chiar și când unele dintre acele coduri de ieșire nu erau zero.
Pe șapte septembrie a plecat o lansare în timp ce rezumatul conținea o linie care spunea bun test packages/gateway/tools exit 1. Citisem contoarele de trecut și de picat ale două suite și nu pe ale celei de-a treia. Nu s-a stricat nimic: testul care pica era o gardă asupra antetelor scripturilor de măsurare, deci produsul nu a fost atins, iar clientul nu a văzut niciodată nimic. Rezultatul a fost în regulă, iar forma a fost greșită, și forma este singura parte care merită consemnată.
Contoarele nu sunt poarta
Motivul pentru care asta este jenant și nu doar ghinionist este că modul de eșec are un nume în propriile noastre însemnări, iar numele este verde în gol: o verificare care nu este legată de o decizie nu ajunge să fie citită, iar o verificare citită cu ochiul este citită așa cum citește un om, adică uitându-se după lucrul pe care se așteaptă să îl vadă.
Rezumatul a fost gândit ca să fie parcurs din priviri. Fiecare suită își tipărește propriile totaluri undeva mai sus, deci mișcarea firească este să te uiți la „4515 pass, 0 fail” și să te simți gata. Codurile de ieșire stăteau cu un ecran mai jos, într-un fișier a cărui ultimă linie spunea DONE.
Reparația are patru cuvinte de shell. După toți pașii, scriptul își caută în propriul rezumat orice cod de ieșire între unu și nouă, iar dacă găsește unul adaugă o linie care începe cu FAIL:. Regula care merge cu ea este că un DONE fără un FAIL este poarta, iar contoarele nu sunt. A doua jumătate a acelei reguli contează mai mult decât prima: un om care are un semnal ieftin și corect va înceta să își construiască obiceiuri scumpe în jurul unuia nesigur.
Există o regulă însoțitoare, anume că o comandă de desfășurare nu împarte niciodată un lanț de shell cu o verificare care doar tipărește. Dacă verificarea nu poate face lanțul să pice, ea este documentație.
Ce era roșu de fapt
Garda care a picat face parte dintr-o familie și merită descrisă fiindcă este neobișnuit de ieftină față de ce cumpără.
Directorul gateway ține scripturile de măsurare: baleieri de browser, sonde de așezare în pagină, unelte de semănat și de curățat, lucrurile pe care o rundă le scrie ca să dovedească ceva și pe urmă le lasă în urmă. Astăzi sunt puțin peste cinci sute. Sunt scripturi simple, fără verificare de tipuri, ceea ce înseamnă că singurul lucru care stă între ele și putrezire este ca cineva să se nimerească să ruleze vreunul.
Patru site trec peste toate într-o suită de teste obișnuită:
- Fiecare script se analizează, folosind chiar runtime-ul care îl rulează.
- Fiecare import relativ se rezolvă către un fișier care există. Aceasta este forma de mort pe care un analizor nu o poate vedea: scriptul este perfect sintactic și cade pe prima linie când îl rulați.
- Fiecare script poartă un antet cu proză în el. Fără o explicație, o unealtă de acum patru runde nu se deosebește de una care mai contează.
- Fiecare antet numește runda din care vine, în una din exact două forme fixe.
A patra era roșie. Trei scripturi din runda de documentație aveau antete, chiar bune, și niciun marcaj de rundă. Acesta este tot incidentul: o convenție de denumire, prinsă de o gardă care există pentru ca cinci sute de scripturi să rămână lizibile, stând într-o linie de rezumat pe care nu a citit-o nimeni.
De ce necunoscut trebuie să coste un hash de commit
A patra sită acceptă două forme și nimic altceva. Ori antetul spune numele rundei cu o dată și de unde a venit acea atribuire, ori spune că runda este necunoscută și poartă hash-ul de commit al primului commit al fișierului, cu o dată.
Numelui nu i se dă voie, explicit, să fie cuvântul necunoscut. Fără acea excludere, „rundă necunoscută, 2026-09-04” este un nume-cu-dată perfect valabil, iar ramura scumpă poate fi ocolită cu un cuvânt. Așa cum stau lucrurile, a recunoaște că nu știți vă costă un hash pe care cineva trebuie să se ducă să îl caute, iar până îl caută de obicei află și runda.
Data este obligatorie dintr-un alt motiv. Un nume simplu este proză, nu marcaj: o propoziție de felul „runda care a atins și munca la antete” conține un nume de rundă și nu afirmă nimic despre fișierul în care se află. Cu o dată atașată devine o afirmație despre acest script, făcută într-o zi anume, care este cel mai mic lucru pentru care poți ține pe cineva răspunzător.
O poartă trebuie să stea la zero
Întrebarea evidentă despre cinci sute de scripturi netipizate este de ce nu le verificăm tipurile. Am măsurat asta, iar răspunsul este instructiv.
Configurația există. Este un instrument, nu o poartă, și nu rulează în scriptul de verificare. Măsurat peste scripturi așa cum erau:
| Configurație | Erori |
|---|---|
| Cu setările stricte ale acestui spațiu de lucru | 3180 |
| Fără strict | 398 |
| Fără strict, după ce un fișier partajat a fost făcut tipizabil | 159 |
O poartă trebuie să stea la zero, iar acele 159 rămase nu sunt în cea mai mare parte defecte. Două familii au fost parcurse și amândouă s-au dovedit artefacte ale tipizării unui cod netipizat: un parametru fără valoare implicită care iese din forma dedusă și o buclă de destructurare peste un tablou amestecat care se lărgește la o reuniune. Douăzeci și șapte dintre ele sunt unul și același diagnostic, despre importul unui fișier TypeScript dintr-un script care rulează sub un încărcător care îl tratează perfect.
Ce a cumpărat instrumentul este pasul de la 398 la 159. O singură linie dintr-un ajutor de browser partajat, un constructor care își atribuie argumentele pe instanță în bloc, ținea în viață 239 de erori, fiindcă orice acces la o proprietate a acelui obiect, în orice script, era nerezolvabil. O singură reparație, 239 de erori duse. Acesta este un randament bun pentru o măsurătoare căreia i se dă voie să fie diferită de zero și este exact felul de randament pe care nu îl obțineți de la o poartă, fiindcă o poartă care nu poate fi satisfăcută ajunge să fie oprită.
Verificarea care își măsura propriul runtime
Ca verificarea de analiză să funcționeze a fost nevoie de două încercări, amândouă picate în feluri care semănau cu probleme de produs.
Prima versiune cerea runtime-ului curent să verifice fiecare fișier. Sub rulătorul nostru de teste, runtime-ul curent nu este runtime-ul sub care rulează scripturile, iar semnalizatorul lui de verificare înseamnă cu totul altceva. Rezultatul a fost 301 de raportări de fișiere stricate sintactic, cu linii de sursă citate ca text de eroare. Fiecare dintre ele era în regulă.
A doua versiune numea explicit runtime-ul corect și tot a picat, altfel: rulătorul de teste recoltează procesele-copil care aparțin unui test, iar de la aproximativ jumătatea listei fiecare apel se întorcea cu o stare nulă. Ucis de un semnal, nu de o eroare de analiză. O gardă care se face roșie la întâmplare nu este o gardă.
Versiunea care funcționează pornește un singur proces-copil care verifică el însuși fișierele, câte opt deodată, în aproximativ nouă zecimi de secundă atunci când s-a măsurat. Ce se afirmă rămâne neschimbat: dacă runtime-ul care rulează aceste scripturi le va accepta.
Nici paralelismul nu este o drăgălășenie. Rulată în serie, verificarea depășea limita de timp pe test, iar o gardă care expiră se citește ca o gardă stricată și nu ca una înceată.
Limita de jos pe care nu o cere nimeni
În acel fișier există un al cincilea test care afirmă doar că sunt mai mult de cincizeci de scripturi de verificat.
Un plimbător de directoare care nu găsește nimic face fiecare afirmație de după el trivial adevărată. Redenumiți directorul, schimbați filtrul de extensie, stricați plimbătorul, și patru site verzi raportează despre o listă goală. Limita de jos este pusă dinadins mult sub numărul real, fiindcă rostul nu este să împiedice curățenia; este să facă vizibilă deosebirea dintre „nu este nimic în neregulă” și „nu a fost examinat nimic”.
Același fișier de test este cinstit în privința limitelor lui, iar acea cinste face parte din proiectare. Niciuna dintre cele patru site nu dovedește că un script mai măsoară ce pretinde. Un selector care nu mai există, o rută care s-a mutat, o coloană care a fost scoasă: toate patru verificările trec. Este un prag, nu o verificare de tipuri, iar spunerea acestui lucru în fișier îl împiedică pe următorul să trateze o rulare verde ca fiind mai mult decât este.
Ce am consemna
- O verificare care doar tipărește nu este o poartă. Trebuie să poată face să pice ce vine după ea.
- Scrieți linia de eșec, nu doar codurile de ieșire. Poarta este un cuvânt pe care cineva îl poate căuta, la un loc fix, nu un număr pe care trebuie să îl observe.
- Nu înlănțuiți niciodată o desfășurare în spatele unei verificări care nu o poate opri. Lanțul este promisiunea.
- Contoarele sunt un rezumat, nu un verdict. Oamenii citesc numărul pe care se așteaptă să îl vadă.
- O poartă trebuie să poată sta la zero. Dacă o măsurătoare nu poate, țineți-o ca instrument și spuneți asta, altfel va fi dezactivată și pe urmă ignorată.
- Faceți scumpă scăparea ieftină dintr-o regulă. Dacă a recunoaște neștiința costă un cuvânt, toată lumea scrie cuvântul.
- O verificare care pică la întâmplare nu este o verificare. Vânați instabilitatea înainte să vă încredeți în semnal.
- Afirmați o limită de jos pentru ce a găsit plimbătorul. Patru verificări verzi peste o listă goală arată exact ca patru verificări verzi.
Ca să fie limpede ce este aceasta: linia de eșec se află în scriptul din depozit și a rulat de atunci peste îmbinări care erau verzi. Nu a fost încă dovedită de o rulare roșie în folosință reală, ci numai de rezumatul față de care a fost scrisă. Cifrele de tipizare vin dintr-o singură măsurătoare făcută într-o zi, iar numărul de fișiere se mișcă în fiecare noapte.
Din munca de platformă la Provibr. O altă gardă care a trebuit să își dovedească propriul zero se află în o excepție de accesibilitate care se măsoară, iar un câmp care ținea în tăcere o intenție în loc de un fapt se află în o dată viitoare nu este un calendar.