Ne javítsa ki a hibát, javítsa ki a rendszert

Grace Hopper megtalálja az első tényleges számítógépes hibát, 1947-ben

A hibák olyan ütők! Csak lógsz, próbálsz írni valami újszerű dolgot, aztán valaki feljön, és azt mondja: „Hé, emlékszel arra a dologra, amelyet korábban írtál, és egyáltalán nem gondolsz rá? Teljesen le van torzulva, és ügyfeleink 100% -a utál téged, még Jennifer! ”

Sőt, még rosszabb, ha kijavít egy hibát, majd hat hónappal később valaki azt mondja: “Hé, azt hiszem, ez a másik dolog is megsemmisült”, és egy csomó időt töltesz rá, és ez alapvetően ugyanaz a hiba, amit én rögzítettem. Ez a legrosszabb!

Ha meg akarom próbálni, hogy a dolgok ne legyenek a legrosszabbok, amikor hibát találok, azt gondolom: „melyik hibaosztály ez?” Logikai hiba? Probléma van a Foo modullal, ha valami helytelennek feltételezi a modul sávját? Typo? Nem megfelelő sorrendben adja át a paramétereket? Félreértés a követelményekkel kapcsolatban?

A hibákat ezen osztályok közül néhányat (különösen az előírásokat!) Nagyon nehéz szisztematikusan kijavítani, de rengeteg elkerülhető hiba található.

Nézzünk meg néhány technikát ehhez!

A rendszer megváltoztatása, így a hiba nem lehetséges

Ez az az okos emberek, akik mindig udvarias, haszontalan kijelentéseket tesznek!

"Kétféle módon lehet megtervezni a szoftvertervezést: az egyik módja annak, hogy olyan egyszerűvé tegye, hogy nyilvánvalóan ne legyen hiányosságok, és egy másik módszer, hogy annyira bonyolultvá tegye, hogy nincs nyilvánvaló hiányosság."
- Tony Hoare
"Ha hatékonyabb programozókat szeretne, akkor rájön, hogy nem pazarolhatják az idejét a hibakereséshez, és nem is vezethetik be a hibákat."
- Edgar Dijsktra

Ez mindig a cél, de én biztosan nem mindig jutok oda, és azt érveltem, hogy nem mindig kell odajutni! Tekintettel a választásra: „tedd ezt a 30 perces változtatást, amely elvégzi a szükséges dolgot, de kevésbé elegánsvá teszi a rendszert” és „töltsön el egy hónapot a rendszerünk újragondolásával, úgy tűnik, hogy ezt a funkciót már a kezdetektől megtervezték”, nagyon kevés ember rendelkezik a luxus az utóbbi szedése. A hajózás fontos, és jelentheti a különbséget a versenytársak és a lemaradás között.

Ennek ellenére az egyszerűsítést mindig mérlegelni kell, mivel az ellenkező probléma is előfordul: végül elmarad, mert a rendszer annyira bonyolult, hogy a változtatások elvégzése rengeteg és hosszú folyamat.

A „kód, ahol egy hiba nem lehetséges” előnyei elég nyilvánvalónak tűnnek, a trükkös dolog azt kitalálni, hogy mikor lehetséges! Íme egy példa, ahol szerintem a rendszer kijavítása volt a helyes hívás:

Van egy Context nevű típus, amely a kérelem hatókörű információkat tárolja (például, hogy mi a felhasználó teszi a kérelmet, függetlenül attól, hogy hitelesítve vannak-e stb.). Különösen ennek a két dolognak, a Tárgy és az Üzenet típusnak nevezett. A MessageType volt a hívott szolgáltatás neve, a Tárgy pedig… bonyolult. Az egyik alkalmazásunkban ugyanaz volt, mint a MessageType, de egy másikban arra derült fény, hogy „az üzenet közvetlenül válaszolok-e valamire, amit küldtem, vagy közvetítés?” Egy közzététel-feliratkozásban. mechanizmusunk van.

Ez hibákat okozott az egész helyben, mert az emberek a Kontextusra nézik és azt gondolják, hogy „hmm ezek közül melyik a legfontosabb, hogy törődjek velem”, és időnként választanák a Tárgyat, amikor a MessageType-re gondolnának, és a kóduk működni fog, de csak az idő egy része. Kód, amely működik, de csak egy idő alatt a legrosszabb típusú kód!

Tehát eltávolítottuk a Tárgyat, és most, hogy feliratkozunk, bezárjuk a nyomon követést arra, hogy „ez egy közvetlen válasz valamire, amit küldtem”. Most ez a fajta „használt Tárgy, amikor a MessageType-re gondoltam” hiba nem lehetséges, mivel a Tárgy már nem egy dolog!

A „hibás sorrendben néhány funkcionális argumentum átadása” osztály szintén nagyszerű jelölt lehet a rendszerváltás javításához. Például nagyon sok helyen átadtuk a lapozásra vonatkozó információkat, például (eltolás int, limit int), és nehéz volt emlékezni, melyik sorrendbe mentünk, ami néhány hibát okozott nekünk az évek során, amikor az emberek rosszul adták meg ezt a parancsot. , tehát most ezeket különálló típusokként adjuk át, és a probléma teljesen eltűnt, mert a fordító ránk ordít, ha rosszul csináljuk!

A hiba összes verziójának begyűjtése statikus elemzéssel

A statikus elemzők olyan programok, amelyek ellenőrzik a programot anélkül, hogy futtatná. Kiderült, hogy sokféle hiba létezik ilyen módon!

Háttérünk Go-ban van írva, tehát van néhány jó lehetőség, amelyet használhatunk. A Go vet a leggyakoribb Go-alapú statikus analizátor, de vannak mások is. Például a Staticcheck-et futtatjuk, és neked is! Amikor bevezetjük, egy csomó régóta fennálló problémát találtunk a kódban, és amikor az egyesítés előtti folyamatos integrációs ellenőrzéseinkhez hozzáadtuk, számtalan további hiba bekerülését akadályoztuk meg. A statikus ellenőrzés és az állatorvos egyaránt nagyszerű, „nulla hamis pozitív” célkitűzésük nagyon könnyű bevezetni őket, mivel a javításhoz szükséges dolgok mindegyike „a kód jobbá tétele” és nem a „kód megváltoztatása valamilyen módon a statikus elemzés megkönnyítése érdekében”. eszközök ”vagy„ adjunk hozzá egy csomó szűrőt, hogy figyelmen kívül hagyjuk ezeket a hamis pozitív eredményeket ”.

Emellett helyesírás-ellenőrzőt is futtatunk karakterláncfájljainkhoz, goimport-okhoz, külön helyesírás-ellenőrzőt forráskódunkhoz, ineffassign, irrevert és gosec fájlokat. Ezeket az ellenőrzéseket kombinálva minden héten tucatnyi kérdést lehet megelőzni - többnyire nitpicky stílusú dolgokat, de valódi hibákat is.

PRO TIPP: Most egy új statikus analizátor bevezetésének a legkönnyebb ideje! Az új, kisméretű projektekhez könnyedén hozzá lehet adni azokat, és a hibák elkerülése érdekében a dolgok növekednek, de minél nagyobb lesz a kódbázis, annál több problémát kell megoldania, amikor új ellenőrzéseket kíván bevezetni. Az Oculus CTO és a programozási varázsló, John Carmack írt erről valamit, amikor a C ++ elemzővel, a PVS-Studio-val kísérletezett, és ez minden bizonnyal igaz nekünk:

„Észrevettem, hogy minden alkalommal, amikor a PVS-Stúdiót frissítik, az új szabályokkal talált valamit a kódbázisunkban. Úgy tűnik, ez azt jelenti, hogy ha elég nagy kódbázissal rendelkezik, akkor valószínűleg létezik minden olyan szinta, amely szintaktikailag legális. Egy nagy projektben a kódminőség ugyanolyan statisztikai, mint a fizikai anyagtulajdonságok - hiányosságok vannak helyütt, csak remélheti, hogy minimalizálja a felhasználókra gyakorolt ​​hatást. ”

Ez nem azt jelenti, hogy ha már van egy nagy kódbázisa, akkor nem adhat hozzá új elemző eszközöket, ez csak azt jelenti, hogy ha hosszabb ideig vársz, az csak nehezebb lesz!

Saját statikus elemzők írása

Mások statikus elemzői nagyszerűek, mert már megírták, de egy másik lehetőségnek, amelyet több embernek fontolóra kell vennie, az analizátorok írása csak a saját adatbázisához!

Hadd mutassam meg egy példát, miért lehet ez hasznos - karakterlánc-lokalizátorunk támogatja a sablonváltozókat a helyettesítéshez, és így néz ki:

str: = loc.T (“Üdvözlet, {{.first_name}}, üdvözlünk a Ligában!”, a térkép [karakterlánc] felülete {} {„first_name": "Reilly"})

Ez egy francia felhasználó számára lokalizálódna a „Bonjour, Reilly, bienvenue à League!” Címre. Az ilyen sablonváltozó használata ilyenkor hasznos, mert lehetővé teszi a fordítók számára egy kicsit több összefüggést, mint hogy csak ott legyen% s, ami befolyásolhatja a mondat fordítását, és ha több paraméterrel rendelkezik, akkor nem kell minden nyelven azonos sorrendben jelennek meg.

Ez a felület azonban hajlamos a hibákra! Itt egy hiba, amelyet néhány héttel ezelőtt találtam a kódban:

str: = loc.T (“Emlékeztető: A (z) {{.title}} -re visszaigazolva vannak a foglalások a (z) {{.date}} napon.”, térkép [karakterlánc] felület {} {„idő”: cím, „dátum”: tm })

A paraméter időtartamának címe kell lennie, amely a felhasználó által lefoglalt találkozó neve.

Ez a hiba kijavítása nagyon egyszerű, és hozzáadhatunk egy tesztet, amely szerint ez a meghívás most helyes, de ugyanolyan könnyű bevezetni ugyanazt a hibát valahol a kódban. Ehelyett javítsuk meg ezt a hibát, örökre!

Ez a T () felület nagyon kényelmes, ezért nem akarom megváltoztatni, de bevezethetünk egy egyszerű statikus elemzőt az ilyen típusú hibák keresésére. Ha kíváncsi vagy, akkor a teljes kódja így néz ki (nagyon sokat behúzva!), De itt van egy összefoglaló arról, amit csinál:

  1. T () nevû függvényt hívunk?
  2. Ha igen, akkor az első argumentum szó szerinti?
  3. Ha igen, akkor érvényes Go sablon? (hibát jelez, ha nem)
  4. Ha igen, térképezik-e a következő argumentumok?
  5. Ha igen, akkor ezeknek a térképeknek a kulcsai megfelelnek-e az első argumentum sablonparamétereinek? (hibát jelez, ha nem)

Az egész 41 kódsorból áll, és ezekből a sorokból csak} van ahhoz, hogy kiszálljanak az összes beágyazott ellenőrzésből. Nem túl rossz!

Most egy ilyen csekk soha nem fog olyan utat találni, mint amilyen a „go vet”, mert a T () nevű függvény szó szerint bármit megtehet, ami Turing-teljes, és biztos vagyok benne, hogy ez a csekk valóban furcsa pozitív eredményeket tartalmaz. kódmintákat, amelyekre nem gondoltam.

DE!

Nem az állatorvosról írok! Olyan dolgokat írunk, amelyeknek pontosan egy adatbázis-alapon kell működniük: a miénk. Ez lehetővé teszi mindenféle feltevés készítését, amelyet nem lehet megtenni egy általános célú eszköz segítségével! Mellesleg, ezért is az, hogy az elemzőkészülék többi része nem nyílt forrású; senkinek másnak a kódja nem hasznos (közzétettem a https://github.com/reillywatson/enumcover oldalt, ami általánosságban hasznos ellenőrzés).

Amint megkapja a go / ast csomag lefagyását, ez a fajta csekk elég gyorsan elkészül. A fentiek talán egy órát vett igénybe az elejétől a végéig, és most már soha nem volt ilyen hiba!

PRO TIPP: ast.Print () a legjobb barátja itt! Soha nem emlékszem az összes különféle csomópontra és arra, hogy miként hívják őket, de nem kell. Csak azt a legkisebb mintaprogramot írok, amelyre gondolok, és futtatom az ast.Print () segítségével, hogy megnézze az elemző fa kinézetét, majd írjam az elemző funkciómat, hogy olyan kódot kapjon, amely úgy néz ki, mintha a mintaprogram program elemző fája lenne. .

Íme néhány további hibafajta, amelyeket ugyanúgy fogunk fel:

  • az fmt.Printf () és a barátok meghívása a termelési kódban (van egy belső naplózó csomag, amelyet inkább használni szeretnénk, ez több környezetet biztosít a hibakereséshez és kezeli a naplófájl forgatását)
  • bizonyos funkciók meghívása, amelyek felületet igényelnek {}, de az argumentumnak reeeeally-nak mutatónak kell lennie (van hasonló check-in go veterán az Unmarshal hívásokhoz, ugyanazt a technikát használjuk, de a saját funkcióink listájával)

A Go következő verziója, az 1.12, támogatást nyújt az egyedi elemzők futtatásához anélkül, hogy az AST-t külön-külön elemezné az egyes írt ellenőrzésekhez (lásd itt további részleteket). Még nem használjuk (a Staticcheck tetejére épített hevedert futtatunk), de azt tervezzük, hogy röviddel az 1.12 kiadása után költözünk hozzá.

Következtetés

A szoftverek nagy léptékű írása nehéz, és a dolgok megváltozásának sebessége azt jelenti, hogy hibák történnek! Ha minden hibát úgy kezelhet, hogy "hogyan történt ez, és hogyan tudjuk megbizonyosodni arról, hogy nem ismétlődik meg", akkor minden hiba lehetőséget ad arra, hogy mindent jobbra tegyünk, ahelyett, hogy csak egy dolgot rosszabbá tennénk.

Ha szereti az okos emberekkel dolgozni, hogy jobb dolgokat készítsen, ez teljesen jó lehetőség, felveszünk felvételt!