Fiecare programator este familiarizat cu bloat. Este peste tot: software pentru întreprinderi care solicită întreprinderii să-și schimbe procesele (alias „de ce cursurile de la Cornell au numere din 4 cifre?”), Software pentru finanțe (de orice fel cu excepția HFT), cadre javascript (cu toate acestea, eforturile de reutilizare a stânga-pad ), backend-uri web (hello there middleware django), RDBMS-uri, sisteme de operare, drivere USB, browsere, plug-in-uri pentru browser, vizualizatoare PDF care sunt de fapt sisteme de publicare a documentelor, aplicații pentru telefon, îl numiți.

Dar echipele de dezvoltatori nu au „panouri de sarcini scrum” cu post-its atașate, pe care scrie „ADD BLOAT”. Celulele dormitoare iraniene nu depun cereri de tragere împotriva proiectelor open source (atunci când serviciile secrete adaugă ușile din spate, așa cum au făcut cu Juniper, par să o facă foarte elegant modificând ușile din spate anterioare - fără balonare!). Deci, cum se umflă software-ul? Cine se află în spatele ei? Ce proces este de vină?

Cine nu este în spatele balonării

umflat

Am găsit vinovatul.

O viziune naivă este că balonarea provine de la dezvoltatori fără idei, care nu prea știu ce fac. Am scris cu toții un cod complicat, mai ales când nu am înțeles destul de bine primitivele care stau la baza lor.

Dar balonarea într-un proiect software important, bine finanțat, semnificativ nu este de obicei rezultatul unei lipsuri de idei. Cred că acest lucru provine din simpla observație că productivitatea software-ului urmează unei distribuții Zipf: o mare parte a codului este de obicei contribuită doar de câțiva programatori competenți, astfel încât cei neclintiți nu au atât de multe oportunități de a face ravagii.

Atunci cine este?

Din experiența mea, software-ul umflă aproape întotdeauna provine de la dezvoltatori inteligenți, adesea cei mai deștepți, care sunt din punct de vedere tehnic cei mai competenți. Cuplați-le abilitățile cu câteva constrângeri interpretate îngust, un efort bine intenționat de a salva ziua (în special, pentru a salva azi în detrimentul zilei de mâine) și voila, avem următoarea poveste.

Povestea structurilor extinse

Poate că cel mai bun exemplu de balonare, care mi-a fost transmis când lucram la o telco mare, implică un comutator de telefon emblematic. Acesta a fost un comutator monstru, capabil să conducă o mare regiune de metrou cu milioane de abonați. Acesta a rulat Unix în centrul său, dar sistemul de operare a fost doar un pic sidehow în comparație cu implementarea monstruoasă a protocolului de semnalizare, zvonindu-se, dacă îmi amintesc bine, că ar avea aproximativ 15 milioane de linii de cod.

Să presupunem că operați pe o bază de cod atât de mare și doriți să adăugați un câmp la o structură. Spuneți, doriți să adăugați un câmp la structura de înregistrare a apelurilor (CDR) pentru a indica dacă persoana apelată se află pe o listă scurtă de prieteni și familie. Lucrul sensibil de făcut ar fi să mergeți la definiția struct și să inserați „uint is_friend_fam: 1;” Asta ar adăuga un pic suplimentar structurii, iar apoi puteți face orice doriți în cod.

Dar când codul este atât de mare și când limita ta de nefuncționare este de 2 ore în 40 de ani, și un tehnician de teren care înlocuia o sursă de alimentare de rezervă în 1987 a apăsat întrerupătorul greșit și a suflat jumătate din bugetul tău de nefuncționare în zona Chicago cu degetele lui grase, nu poți schimba doar dimensiunea structurii. Deoarece acest lucru s-ar putea schimba în cazul în care sunt alocate structurile CDR, cât spațiu suplimentar există în jurul obiectului CDR și ce se întâmplă cu codul care scrie greșit trecutul struct. Acest lucru ar avea consecințe nespuse, imprevizibile, niciuna dintre ele bune.

Așadar, dezvoltatorii de switch-uri au venit cu o idee absolut strălucitoare. Îți dau un moment să te gândești la ce ai face într-o situație similară. Puteți presupune că codul urmează o arhitectură stratificată, tipică codului de rețea.

Ok, vezi dacă soluția ta urmează următoarea soluție strălucitoare:

Calea spre iad este pavată cu bune intenții și trucuri inteligente.

Deci, intrați în definiția struct CDR. Găsiți câmpul care pare a fi cel mai puțin important și cel mai puțin utilizat în general și asigurați-vă că nu este utilizat deloc sub stratul dvs. din stiva de apeluri. Să presupunem că CDR conține ceva numit "uint inap_ain23" care este utilizat doar deasupra stratului dvs. Nu trebuie să aveți nicio idee despre ce este sau ce face inap_ain23. Ceea ce faceți este să salvați valoarea stocată în inap_ain23 când fluxul de control trece prin stratul dvs. Deci, sub stratul dvs., „inap_ain23” nu mai este. Tocmai l-ai „refăcut”. Acum este „is_friend_fam”. S-ar putea să-l aliasă astfel „#define is_friend_fam inap_ain23” pentru a vă ușura lucrurile. Și, în plus, ai niște biți în plus! Primă!

Singurul lucru de care trebuie să vă asigurați este că plasați un anumit cod pentru a intercepta fiecare cale de cod de la straturile de mai jos până la deasupra dvs. Deoarece pe acele căi, trebuie să rămâneți înapoi cu orice valoare ați găsit în „inap_ain23” înainte de momentul în care ați fost chemat. Dacă nu faceți acest lucru, cu siguranță comutatorul se va rupe.

Rău la rău

Așa că a fost rău, dar se înrăutățește. Este aproape imposibil să reiați fiecare cale de control. Cineva va aluneca și va lăsa bitul prietenilor și familiei acolo unde ar trebui să fie informațiile de control pentru baza de date, ceea ce poate provoca un accident masiv. Deci, inginerii au avut un proces a cărui sarcină a fost să scaneze prin structuri de date, în heap-ul sistemului live și să verifice dacă invarianții precum „inap_ain23 trebuie să conțină un număr de port, cu excepția cazului în care bitul superior este 1” și așa mai departe. Și atunci când a detectat o încălcare invariantă, acest proces ar repara structurile de date cât mai bine posibil într-un efort de a evita timpii de nefuncționare. Permiteți-mi să repet: ei ghiceau ce ar trebui să conțină câmpurile și doar le-au reparat.

De la mai rău la Badass

Deci, „redefinirea” unui câmp pare destul de urât. Trebuie să salvați câmpul într-o zonă auxiliară, poate pe stivă, poate în altă parte a grămezii, departe de structura CDR prețioasă și inviolabilă și nu uitați să o restaurați pe fiecare cale de rezervă. Veți suporta penalizarea de performanță a două scrieri la fiecare apel și, de asemenea, la fiecare retur. Cineva trebuie să efectueze o verificare dinamică prin grămadă pentru a prinde cazurile în care alunecați.

Dar aceasta nu este cu siguranță partea deconcertantă a poveștii. Asta urmează.

Imaginați-vă ce se întâmplă atunci când acest inginer strălucit aruncă la masa de prânz despre trucul ei rece de câmpuri refacute. Imaginați-vă ce se întâmplă la scară.

Nu a durat mult, a făcut-o?

Zvonul este că, în acest comutator, la aproape fiecare apel de funcție cu straturi încrucișate, un cod strălucitor ar salva și reutiliza un câmp și, la fiecare întoarcere, ar restabili vechea valoare, ca un fel de joc de shell ciudat, unde niciun câmp dintr-o structură de date nu este vreodată ceea ce este etichetat.

Și ce domenii, credeți, s-ar mulțumi inginerii noștri geniali pentru acest joc shell? De ce, desigur, că inap_ani23 sigur nu pare atât de important. Deoarece oamenii străluciți au sfârșit prin a reutiliza aceleași câmpuri, „nu foarte frecvent utilizate”, s-a dovedit că cele mai inofensive, mai puțin importante căutări din structurile de date critice ale comutatorului erau de fapt cele mai importante, cele accesate cel mai puternic.

Asta nu se întâmplă acum, nu?

Mi s-a amintit de această poveste de telco, în timp ce dezvoltatorii Bitcoin s-au gândit dacă ar trebui să crească în mod eficient dimensiunea unui bloc bitcoin printr-un mecanism „soft-fork”. Nu vreau să reformulez softul Bitcoin vs. dezbateri dificile aici, dar unele informații sunt în ordine.

În esență, unii dezvoltatori Bitcoin iau în calcul un truc în care reutilizează tranzacțiile „oricine poate cheltui” pentru a sprijini ceva numit martori segregați. Pentru versiunile mai vechi ale software-ului Bitcoin desfășurat în sălbăticie, se pare că cineva aruncă bani, literalmente, în aer într-un mod în care oricine îl poate apuca și îl poate face al lor. Cu excepția versiunilor mai noi de software, asigurați-vă că numai persoanele vizate îl captează, dacă au tipul corect de semnătură, separat corespunzător de tranzacție, astfel încât să poată fi transmis, validat și stocat sau aruncat, în mod independent. În mod uimitor, vechiul software moștenit, care este dificil de schimbat, vede că banii au fost aruncați în aer și luați de cineva, în timp ce noile software-uri știau de-a lungul timpului că ar fi putut fi ridicate doar de destinatarul său. Este, după fiecare metrică, o idee foarte inteligentă și am un respect extraordinar pentru oamenii care au venit cu ea. Cea mai mare parte a creierului meu simte că acesta este un truc strălucit, cu excepția neuronilor deja-vu care țipă cu „acesta este exact același truc de reutilizare ca la comutatorul telefonului”. Este doar între versiunile software dintr-un sistem distribuit, spre deosebire de diferite straturi dintr-un singur sistem de operare [1].

Costul complexității

Păstrarea trucurilor inteligente în afara software-ului este imposibilă și probabil nedorită. Dar este crucial să înțelegem costurile, astfel încât să le putem cântări în mod eficient în raport cu beneficiile.

Pe măsură ce sistemele se umflă, efortul necesar pentru a le înțelege și a le schimba crește exponențial. Este greu să atragem noi dezvoltatori interesați de un proiect software dacă îi obligăm să nu învețe doar cum funcționează, ci și cum a ajuns acolo, deoarece procesul său de evoluție este atât de critic pentru forma finală în care a ajuns. Au devenit ingineri tocmai pentru că nu erau buni la istorie.

Dacă un dezvoltator nu ar fi urmat să dezvolte o anumită bucată de software, va găsi starea finală a codului dezagreabilă, în contradicție cu modul în care un inginer inteligent l-ar fi proiectat dacă ar fi făcut-o de la zero. Deci, vor fi mai puțin probabil să se angajeze și să stăpânească platforma. Acest lucru nu este bun pentru proiectele open source, care au nevoie constantă de dezvoltatori mai capabili. Nici nu este bun pentru sistemele în care un efect de rețea este crucial pentru succesul sistemului.

Discuția Bitcoin segwit a fost exprimată în termeni de furci dure versus moi, cu multe argumente rezonabile de ambele părți. Sper că această discuție arată clar că nu este doar soft vs. furci dure, dar sunt și furci moi față de interesul scăzut al viitorilor dezvoltatori. Acest tip de trucuri inteligente apar, nu o datorie tehnică, ci o datorie socială care se acumulează strict în timp.

Poziția mea personală pe frontul Bitcoin este că sunt în regulă cu segwit, dar acest lucru folosește alocarea pe viață a Bitcoin a hacks inteligenți - orice complexitate suplimentară va retroga Bitcoin în aceeași categorie ca codul de comutare telco.

Din fericire, complexitatea și balonarea sunt adesea propriul lor remediu: după un anumit prag, oamenii doar renunță la sistemul balonat și trec la platforme mai curate și mai elegante.