Amice, ești idiot!
N-ai cu ce, mă! N-ai cu ce!
Published on January 21, 2020
2020 haranguesAbout 9 minutes of reading.
În loc de „nu știu alții cum sunt”, eu știu foarte bine cum sunt alții. Unii sunt proști, alții tolerabili, iar alții sunt buni - mai buni decât mine. De la cei din urmă - adică cei mai buni - vreau să învăț, să nu fac greșelile pe care le-au făcut ei, care va să zică să fiu eficient. Pentru că eficiența este o parte integrantă a legii conservării energiei, drept pentru care a fi ineficient reprezintă - în capul meu - a fi împotriva curentului.
O poveste mai veche
În vară am reușit să intru într-un așa zis conflict datorat unei probleme de securitate.
Ca să descriu în limbaj trivial, în momentul în care un client (persoană fizică sau server) se adresează unui server cu o cerere, este necesar să facă dovada că el are dreptul să facă acea operațiune. În cazul nostru, a fost ales standardul ce poartă numele de JWT - nu o să expun motivele aici.
La autentificare, clientului i se înmânează o cheie (token) de către server, criptată cu un secret cunoscut doar de către server. În informația criptată se depozitează informații cum ar fi identificatorul unic al utilizatorului, pentru a evita cazurile de impersonare (de exemplu, mă autentific la bancă cu userul și parola mea, dar cererile de plată le fac cu o cheie care conține identificatorul unic al altcuiva - drept consecință, dacă serverul nu verifică impersonarea, apar fraude). În cazul din această poveste, pe lângă informația despre utilizator sunt prezente - cu titlul opțional - anumite informații despre context.
Ei, ce-au făcut „turiștii” care au lucrat la acest proiect:
-
respectiva cheie este polimorfică, însemnând că în funcție de context anumite informații sunt prezente sau nu. Atunci când nu sunt prezente, ele lipsesc cu desăvârșire, ceea ce schimbă structura de date, în sensul că respectiva cheie este inutilizabilă în servicii care nu o pot decoda - ele știu o structură și numai una. Nu ar fi nimic în neregulă, dacă serviciile nu ar trebui să comunice între ele. În momentul în care un client al serviciilor cheamă un proxi (un serviciu care cunoaște alte servicii și care le „cheamă” în dreptul clientului), acest proxi ar trebui să folosească cheia de securitate pe care a trimis-o clientul. De ce? Păi e simplu: trasabilitate. În cazul în care vrei să știi ce vânzător a vândut ce, sau ce administrator a modificat prețul ăla și când, este nevoie să trimiți token-ul ăla așa cum a venit de la aceste persoane.
-
respectiva cheie este generată pentru fiecare dintre microserviciile pe care clienții le pot chema direct. Nu pare nimic rău în asta, până când utilizatorii se plâng că procesul de login este încet - e și normal să fie așa. De fiecare dată când se criptează o cheie, se folosește intensiv procesorul. Acum sunt 10 chei ce sunt generate - mă întreb ce fel o să arate lucrurile când vor fi o sută. Sau mai mult.
-
tot cheia este întoarsă în răspunsul de la login, dar nu unde trebuie, adică în header-ele HTTP. Este întoarsă în payload. Deși standardul spune altceva. Dar, lasă că știm noi mai bine.
Măsurile pe care le-am propus sunt simple:
-
folosirea unei structuri unice pentru cheia JWT. Dacă există informații opționale, acestea pot avea valoari implicită - astfel, nu este nevoie să facem subspecii și putem deschide spre consum anumite microservicii fără a fi necesar să scriem cod suplimentar.
-
cheia JWT se transmite către utilizatori în header-ul HTTP, acolo unde-i este locul. De ce? Păi în primul rând pentru respectarea standardelor. În al doilea rând pentru că răspunsul „utilizabil” trebuie să fie cât mai curat și cu informația cât mai condensată. Ce să mai spun: nu amestecăm capra cu varza și culoarea butonului…
-
ca măsură de securitate, secretul cu care este criptată cheia se poate schimba automatizat (pentru absolut toți actorii participanți) la un interval de timp, așa cum este normal. În acest moment, cheile respective trebuie schimbate la mână și dacă ceva se schimbă în Matrix, atunci el trebuie schimbat și în Matrix II. Ce să vezi: Matrix II depinde de Matrix III care are o altă cheie și uite-așa ne împușcăm singurei în piciorușe.
-
în timp, să se adauge verificare impersonării în locurile în care acest lucru este critic, ceea ce acum nu există. Și nu există pentru că suntem la Grădinița SRL, unde folosim id-uri din baza de date ca pe constante universale, alături de Bolzman și viteza luminii.
Răspunsul a fost „nu!”. Deși mi s-a dat dreptate, au motivat că e prea mult de lucru. Eu asta nu am înțeles niciodată: ce înseamnă prea mult de lucru? Când rezolvăm o gașcă atât de mare de probleme (poate nu le-am scris pe toate aici, dar sunt sigur că se văd multe dintre implicații), ce dracu’ înseamnă prea mult de lucru?
Acum o săptămână, episodul s-a repetat: mi-a fost cerut să adaptez codul „nou” - care ar fi trebuit să evite toate prostiile făcute în trecut - la codul „vechi”, adică ăla care e conform cu prostiile și amatorismele unor „turiști” cărora li s-a arătat unde să se pișe și s-au pișat. M-am opus din nou, făcând cunoscute argumentele de mai sus. Mi s-a dat iar dreptate (am saci întregi în balcon), fără însă să facem ce trebuie.
Amice…
Deși ar fi trebuit să-mi fie învățătură de minte, nu mă pot obișnui cu direcția în care îmi este indicat să merg. Și nu e o chestie personală. Și nici de orgoliu. Mie îmi pare o chestie de eficiență. Mai bine zis, de ineficiență. Am fost adus pe post de expert. Sunt folosit pe post de „turist”. Să-mi văd de treabă, să scriu cod… Whatever that means!
Este o dovadă de incongruență, să-i spui unui om că este cea mai experimentată resursă într-un domeniu și-api să-i ignori toate propunerile pe care le are. Și când spun toate, adică toate. Deși ulterior, se conving că am dreptate, de obicei e prea târziu: munca necesară să aduci totul la nivelul la care putea să fie dacă propunerea era acceptată, este consistentă. Mai rău este că dau dovadă de prostie, uitând că de fapt ceea ce adoptă a fost propus cu ceva timp în urmă.
… ești idiot
Astăzi, deschid emailul și ce să vezi…
Un pic de preambul: interfețele în Go sunt dificil de înțeles. Ele nu sunt un contract, așa cum sunt în alte limbaje. Sunt mai degrabă definiția a ceea ce „cineva” poate să facă. Motivul care a stat la baza acestui design e simplu: nu e nevoie să declari că „cineva” implementează interfețele X, Y și Z. Este suficient ca acel „cineva” să aibă semnătura corectă.
Acest „feature” al interfețelor în Go te ajută să te concentrezi la ce ai de făcut, nu la legăturile - de obicei restrictive - dintre componente. Este ca și cum ai spune, „dacă ceva poate face asta, atunci el poate fi folosit aici”, fără să știi toate detaliile despre acel cineva. Am văzut de multe ori declarații de interfețe largi în Go: se cunoaște de la o poștă că programatorul respectiv are un mind-set
diferit - vine din Java, PHP sau Python - pentru că dorința asta de over-engineering
(class all the things
) nu poate veni din altă parte. Știu asta : am fost acolo.
Poate ar trebui să scriu o întreagă poveste despre asta - Be conservative with what you do, be liberal with you accept - într-o postare separată.
Înapoi la emailurile noastre: mi-a fost refuzat un pull-request pentru un client care ședea lângă server-ul său, pe motivele următoare:
-
clienții pot fi scriși în orice limbaj, drept pentru care clientul pentru Go nu furnizează un beneficiu pentru orice consumator al server-ului respectiv
-
versionarea clientului nu poate fi manageriat separat de versionarea serviciului care furnizează API-ul
Respectivul mesaj se încheia cu: „dacă ai chef de vorbă pe subiectul ăsta, adresează-te lui cutărică.”
Pam-pam!
Care va să zică, mon cher: dacă definești clientul unui server, în locul în care definești și serverul obții următoarele lucruri:
-
cei care folosesc clientul, folosesc un singur constructor, fără să le pese de implementarea propriu zisă - ceea ce este de dorit, nu?
-
dacă interfața evoluează, așa cum am scris mai sus despre interfețele în Go, aceasta poate fi
embedded
(nu einheritance
) într-o nouă versiune (v1 esteembedded
în v2) și rămânem fără necesitatea de a versiona vreun client, vreodată în viața asta. Va fi doar despre ce poate să facă clientul v1 față de ce poate să facă clientul v2. -
noi, ăștia elitiști, scriem teste. Când scrii teste pentru server, ai nevoie de un client. Deci, oricum ar fi, îl definești și-l mentii
up-to-date
. Ok - și dacă faci chestia asta, de ce să nu-l folosești și pentru alte servicii/clienți scriși în Go, care au nevoie de un client pentru respectivul server?
Dacă e de neglijat or ba, rămâne de văzut.
Oricum, „clienții pot fi scriși în orice limbaj, drept pentru care clientul pentru Go nu furnizează un beneficiu pentru orice consumator al server-ului respectiv” nici măcar nu e un argument. E o bășină pe post de argument. Despre versionare, numai de bine: mă bucur că avem așa instrucțiuni de implementare - nu înseamnă altceva decât că o să avem de lucru continuu. Pentru orice adăugare de funcționalități va trebui să scriem de două ori mai mult cod, să facem două deployment-uri, să vedem cum se pupă între ele și alte chestiuni de proces de dezvoltare de software.
Păi ce să mă mai adresez lui cutărică, când decizia a fost luată? Nici măcar n-am fost întrebat, deși am vorbit despre tema asta neîntrebat. La ce dracu’ mai spui că știu cel mai bine Go, dacă tu nu mă lași să-mi fac treaba de consultant?
În loc de concluzie
Bineînțeles că m-am supărat!
Știam de foarte mult timp că sunt la „Grădinița SRL”.
Mi s-a cerut răbdare. Am avut!
Mi s-a cerut să contribui cu idei, standarde și propuneri. Am făcut-o!
Suntem însă pe același drum, unul al imposturii și al prostiei la rang de arhitectură. Mai bine zis „arhite-n-gură”.
Mi-am propus să nu mai contribui. De niciun fel! Dar n-o să pot, pentru că nu-mi place direcția în care duce acest lucru. Renunțarea la principii nu e ceva care se poate face cu ușurință, la o apăsare de buton. O să-mi rămână, așadar să înjur aici, în continuare, fiind o metodă de descărcare, până reușim să mai concediem din impostori.
Nu-mi plac impostorii dintr-un motiv organic: ajung curând să mă îndoiesc de ceea ce știu că știu.
De asta spun: „Amice, ești idiot!”
Related
Amice, ești impostor!
N-ai cu cine, mă! N-ai cu cine!
Published on January 8, 2021
2021 haranguesAbout 8 minutes of reading.
Anul 2020 și rezoluții pentru 2021
Published on December 27, 2020
2020 ReviewAbout 8 minutes of reading.