Docblocks sunt inutile. Change my mind 🤪

Eu sunt adeptul „cât mai puțină documentație inline”. Asta pentru că am învățat că un cod ce necesită explicații suplimentare poate fi, de cele mai multe ori, simplificat. Variabilele pot avea nume sugestive, la fel și constantele sau metodele. Sigur, sunt cazuri în care rezultă o bucată de cod foarte arcane și ăla e singurul mod în care poți face lucrurile.

Plus că mai există și situația în care comentariile nu îmbătrânesc frumosc, ca să zic așa. Adică una zice comentariul, alta zice codul…

Cu toate astea, tot întâlnesc genul ăsta de comentarii și serios că nu le înțeleg rostul în viață. Adică este cineva care se uită la cod, vede public $page_settings_manager și se întreabă:

Hmmm, oare ce vizibilitate are variabila asta? Știu! Mă duc în docblock!

La metode sunt informații utile (@param și @return) și, împreună cu @version cred că se cam termină lista de atribute utile. Îmi scapă ceva?

/**
 * Page settings manager. <<<< numele variabilei
 *
 * Holds the page settings manager. <<<< din nou numele variabile
 *
 * @since 1.0.0
 * @access public <<<<< variabila are atributul ăla deja
 *
 * @var Page_Settings_Manager <<<<< din nou numele variabile. Și spunem că este o variabilă.
 */
public $page_settings_manager;

La fel și aici:

/**
 * Instance.
 *
 * Holds the plugin instance.
 *
 * @since 1.0.0
 * @access public
 * @static
 *
 * @var Plugin
 */
public static $instance = null;
2 Likes

Daca folosesti un IDE modern, cand faci autocomplete sau cand dai hover peste o variabila/un constructor iti va arata ce face din documentatie in cod. Poate te-ar deranja daca ar lipsi autocomplete-ul cu documentatie la o functie pe care ai scris-o (OCD).

Nu ii ajuta pe altii, dar te poate ajuta sa memorezi mai bine functiile/variabilele pe care le folosesti, in plus este un task simplu care te ajuta sa incepi sa lucrezi la cod cand nu stii cu ce sa incepi. La TypeScript poti pune si in comment-uri tipurile la variabile. In limbaje in care nu ai abstracte/interfete/tipuri pui in comentariu structura si tipurile ca dupa sa nu trebuieasca sa faci reverse engineering sau sa intrebi clientul ce trebuia sa primeasca un anumit field.

public static cred ca le poti folosi doar in clase, daca scrii o librarie fara clase nu se mai intelege ce e public sau disponibil in scope pentru alte functii fara sa inventezi tu o conventie a numirii variabilelor. In Go de exemplu mi se pare foarte faina conventia numirii variabilelor in functie de pattern-uri si utilizare, dar tot se folosesc comentarii in special la interfete/structuri.

3 Likes

Cateva lucruri pentru care docblocks sunt foarte utile:

  1. Sa vezi ce exceptii se arunca intr-un bloc de cod fara sa trebuiasca sa parcurgi tot
  2. Sa vezi ce preconditii sau postconditii poti avea, daca este cazul
  3. Sa vezi ce presupozitii sau efecte secundare are o bucata de cod, daca este cazul (gen: /// Warning! This method implicitly calls GetSafeWindowHandleW behind the scenes, please ensure that the return value is correctly disposed when no longer needed!), lucruri la care poate nu ai acces sau sunt usor de trecut cu vederea in mod eronat
  4. Sa vezi garantiile de executie (gen thread-safety, immutability, state integrity, etc.) pe care ti le ofera o bucata de cod, garantii care sunt excesiv de complicate sa le evaluezi din ochi de fiecare data
  5. Sa vezi care este efectul unor parametri, in particular switch-uri, si daca anumiti parametri pot sau nu sa fie ignorati (cam tot ce are legatura cu COM/COM+ intra in aceasta categorie)
  6. Sa vezi daca exista diferente de platforma (gen: // This method simply returns “false” on a Linux machine, since the Registry is unavailable, and all out variables are set to their default value)
  7. Sa vezi daca exista o ratiune pentru care un cod este scris intr-o anumita forma probabil contraintuitiva (gen: // This method repeats validation due to a bug in EF6.1 with properly locking a set’s internal state, please do not change this)
  8. Sa intelegi algoritmul din spate, in momentul in care este foarte lung, foarte complicat sau nu e disponibil codul sursa
  9. API documentation
  10. Sa pastrezi docblock-ul peste web service-uri, acolo unde e posibil, si unde e rezonabila presupunerea ca developer-ul va avea acces doar la o parte limitata de cod (a se vedea servicii SOAP, WCF, etc.)

Docblocks sunt complet inutile:

  1. Cand un tool automat de gen GhostDoc sau DocFX poate genera documeentatie perfecta care nu are nevoie de nici o schimbare de la developer inainte sa fie pusa in productie (care, coincidenta face, sunt fix genul de exemple ce le-ai dat tu)

Have I changed your mind yet?

5 Likes

@adimosh: foarte multe din punctele tale sunt foarte… desktop-oriented. Sau măcar legate de un limbaj compilat.

Este adevărat că nu am zis nimic de limbaj; cumva, se întâmplă să uit că există și altceva în afară de limbaje de scripting și că nu tot timpul poți să te uiți pur și simplu în sursă, să vezi ce se întâmplă acolo.

Probabil am avut noroc în viață și am dat doar de comentarii ca cele date ca exemplu de mine mai sus, de unde și înverșunarea
mea :slight_smile:

Parțial :smiley:

genul ala de comentarii ajuta ide-ul. dpmdv sunt singurele comentarii care au sens. in rest, pt majoritatea argumentelor aduse de adimosh, probabil este e ceva in neregula cu codul.

In Go de exemplu comentariile inline sunt esentiale pentru ca din ele se genereaza documentatia (de asemenea sunt folosite de IDE). Nu cred ca s-ar putea lucra fara ele.

Nu stiu ce ar fi in neregula cu codul daca prezinta garantii de executie, sau valideaza parametri, sau exista o ratiune pentru care e scris intr-un fel contraintuitiv, sau daca exista diferente in executie intre platforme.

OK, that sounded like a challenge :slight_smile:

Pentru scripting languages, docblocks sunt utile pentru:

  1. A notifica daca exista incompatibilitati (gen // Importing this module is not possible if qi_bittransfer has already loaded on the same page)
  2. A notifica daca anumiti parametri trebuie sa fie intr-un anumit fel pentru a se putea lucra cu ei (in special la limbaje fara strong types)
  3. A notifica daca trebuie incarcate si alte module/script-uri pentru ca o anumita functionalitate sa fie activa
  4. A scoate in evidenta diferentele dintre platforme, unde e cazul (think early days of jQuery)
  5. A explica ca anumite diferente de limbaj exista intre platforme (ex. CSS cu extensia “moz”) si ca e important ca ele sa fie luate in considerare
  6. Sa vezi daca exista o ratiune pentru care un cod este scris intr-o anumita forma probabil contraintuitiva (valabil si aici ca si la compiled languages)
  7. API documentation (la fel)
  8. Sa evidentiezi schimbari, daca este cazul - din moment ce codul nu este neaparat compilat, inseamna ca o schimbare in cod poate sa il faca neexecutabil in anumite situatii, si nu o sa ai un copilator care sa te anunte senin ca “No overload for method X(…) and no extension method could be found with that name and list of parameters”
  9. Sa avertizezi despre mutatii in state-ul obiectelor acolo unde e posibil si e cazul (gen “Vezi ca de acum incolo, x.ProprietateaMea intoarce string, nu int”)
  10. Sa intelegi algoritmul din spate, in momentul in care este foarte lung, foarte complicat sau nu e disponibil codul sursa (la fel ca la compilate), sau codul sursa e minificat sau obfuscat

Merge?

1 Like

what?..

pai cum vezi comentariile daca n-ai acces la cod?

cum am spus, majoritatea punctelor de acolo ar trebui indica o calitate a codului extrem de proasta

OK, exemplul meu a fost foarte tras de par, recunosc, desi in cam toate limbajele de programare poti sa faci asta (inclusiv un limbaj strongly-typed gen C# are tipul “dynamic” care poate sa iti ofere ce vrei inauntru).

Limbajele de programare care nu sunt strongly-typed pot sa aiba orice fel de return al unei functii, lasandu-se la latitudinea caller-ului cum anume se interpreteaza. Insasi faptul ca exista pe StackOverflow thread-uri de genul:

sau

sau

sunt suficienta dovada ca ceea ce am scris are sens.

Exista o expresie: Just because you can doesn’t mean you should. Ea are un corolar: Just because you shouldn’t doesn’t mean you won’t have to. Exemplul dat se incadreaza exact in acest corolar, si nu este o problema de calitate a codului extrem de proasta, dupa cum se va vedea putin mai jos.

Prin multe metode, in functie de care e scopul codului.

Ai, de exemplu, documentatie in WSDL pentru web service-urile pe care doresti sa le expui. Aici prea multe exemple nu pot sa dau pentru ca nu am avut timp sa cercetez cum se intampla in alte medii de dezvoltare, insa pentru servicii bazate pe .NET, exista utilitare care sa iti decoreze WSDL-ul pornind de la docblocks, ca de exemplu proiectul WCFExtras.

Dupa aceea, ai generare automata de documentatie din docblocks. Aici amintesc Doxygen, JSDoc Toolkit si Sandcastle ca si tool-uri care fac asta, plus o lista aici.

Dupa aceea, ai suport integrat in editoare (ma rog, pentru cei care folosesc ceva mai eficient ca vi sau notepad pentru a scrie cod) Exista suport in cam toate IDE-urile majore, MS Visual Studio, Eclipse, NetBeans, inclusiv Notepad++ are parca avea plugin pentru JSDoc. Codul compilat in cam toate formatele vine la pachet cu multe metode de a distribui docblocks, de la anotare directa pana la fisier extern de XML documentation.

Tine minte: este vorba despre docblocks - nu este logic, in mediul actual de development, sa te astepti ca doar developerii care au acces la tot codul si trebuie sa il lucreze sa apuce sa le vada.

Pai hai sa le luam pe rand, ce zici?

  1. Sa vezi ce exceptii se arunca intr-un bloc de cod fara sa trebuiasca sa parcurgi tot

Eu consider ca este util ca intr-un docblock sa ai informatii despre ce exceptii se pot arunca in ce situatii. Functia pe care o documentezi s-ar putea sa nu arunce un anumit tip de exceptie, insa o alta functie peste ea in call stack sa o arunce, iar developer-ul sa o includa in documentatie. Asta indica faptul ca acele conditii care cauzeaza exceptia documentata sau sa fie independente de codul functiei documentate, sau verificarile pentru ele sa fie suficient de laborioase sau inoportune incat developer-ul sa le lase in subordinea functiei apelate. Le poate lasa chiar din ratiuni de performanta: daca anumite conditii oricumn vor fi verificate cand va fi nevoie, care e motivul pentru care le-as reverifica acum? Documentarea unui caz de exceptie in continuarea stack-ului va ajuta chiar si programatorii care au acces la tot codul, din simplul motiv ca nu mai trebuie sa parcurgi mental n-sprezece nivele de call stack ca sa iti dai seama care sunt posibilele exceptii si cum sa le tratezi.

Din punctul meu de vedere, aceasta nu indica o calitate proasta a codului, ci o calitate buna a codului, si a developer-ului care a scris docblock-ul.

  1. Sa vezi ce preconditii sau postconditii poti avea, daca este cazul

Preconditiile ar trebui sa fie evidente (validare de parametri), dar si postconditiile documentate maresc performanta. Un developer poate sa nu realizeze o postconditie din multe ratiuni (una dintre ele fiind ca, desi are acces la cod, algoritmul este complex, iar anumite postconditii nu sunt imediat evidente din el). In cel mai rau caz, il vor scuti de anumite verificari inutile ale return value cand o foloseste.

Departe de a indica o calitate proasta a codului, acest tip de documentare indica de regula o calitate foarte buna a codului.

  1. Sa vezi ce presupozitii sau efecte secundare are o bucata de cod, daca este cazul

Nu stiu tu, insa daca o metoda pe care o folosesc apeleaza, undeva in sus pe call stack, o metoda care are efecte secundare serioase, sau care depinde de anumite seturi de permisiuni, eu as vrea sa stiu asta, de preferabil fara sa petrec ore in sir examinand cod dependent, si, de preferabil, inainte sa ajunga la testeri. Si este o cerinta perfect normala, spre exemplu, in orice software care necesita sa apeleze functionalitati de baza ale OS-ului, sau functionalitati restrictionate.

Operatiuni care par banale (gen comunicarea prin socket-uri) se folosesc discret de functii de baza ale OS-ului, care au si efecte secundare (a se vedea semafoarele) sau restrictii (a se vedea file access). Este aceasta o problema cu calitatea codului? Nu, bineinteles. Este doar un scenariu normal de utilizare.

  1. Sa vezi garantiile de executie (gen thread-safety, immutability, state integrity, etc.)

Am avut la un moment dat, lucrand pe o aplicatie de procesare de tranzactii financiare, o discutie cu arhitectul respectivei aplicatii. Imi facea un review, si m-a intrebat la un moment dat: “De ce ai apelat metoda asta intr-un bloc lock? Metoda e thred-safe.” (nota: pentru cei nefamiliari cu .NET: un bloc lock se asigura ca ce este in interiorul blocului nu poate fi apelat decat de un singur thread odata). Eu am raspuns “Pai nu aveam habar ca e thread-safe, nu scrie niciunde.”

Ce a urmat a fost o analiza mai amanuntita a metodelor care sunt thread-safe din acea aplicatie, o puricare a documentatiei, si directiva biroului de arhitectura a fost ca obligatoriu toate metodele care sunt thread-safe sa fie documentate ca atare. Dupa aceasta, au urmat 2 zile in care, acolo unde era fezabil, s-au scos toate lock-urile peste metode thread-safe. Server load a scazut cu 5% la urmatorul deployment (apropos, 5% nu pare mult, dar pentru o aplicatie serios optimizata, care lucreaza pe sute de servere in paralel, si unde s-a invesstit enorm in eficienta, 5% doar din sincronizare de thread-uri este enorm).

Concluzia? Simpla adaugare a “This method is thread-safe.” in documentatie poate salva milioane de computing hours in viitor. Departe de a indica o calitate slaba a codului, daca ai ajuns in starea in care aplici punctul acesta din convingere, codul tau a ajuns la o calitate foarte buna (gen top 1%).

Voi sari peste 5, caci justificarea am dat-o direct acolo, si ar trebui sa fie clar ca nu are nici o legatura cu calitatea codului.

  1. Sa vezi daca exista diferente de platforma

Neica, daca pe Linux nu mi-e disponibil Windows Registry, indica asta o calitate proasta a codului? Sau indica doar ca Linux nu functioneaza in acest fel? Cu toate astea, daca am o metoda care este platform-specific pentru ca nu are cum sa fie altfel, nu crezi ca as vrea, ca developer, sa stiu asta cumva?

  1. Sa vezi daca exista o ratiune pentru care un cod este scris intr-o anumita forma probabil contraintuitiva

Aici se poate, intr-adevar, discuta despre calitatea codului, gen cod contraintuitiv ca sa fie mitigare la un bug (ergo, calitate proasta a codului, doar ca nu neaparat al celui documentat).

Insa trebuie inteles ca aceasta situatie apare foarte des in practica din ratiuni ce nu tin de programare in sine. Exista situatii in care o modificare legislativa impune sa ai o metoda de genul GetTotalTaxAmount(int transationID), dublata de o metoda GetTotalTaxAmountForQuebec(int transactionID). In acest caz, ar fi bine de stiut, din documentatia primei metode, ca, daca vrei sa o aplici lla o tranzactie din Quebec, ar fi bine sa apelezi cealalta metoda, datorita diferentelor fiscale. N-are treaba cu calitatea codului, insa tot trebuie documentata pentru asta.

In aceeasi ordine de idei, gandeste-te ca acest caz este unul in care diferenta e usor si imediat observabila. Ce te faci, insa, cand ai un sistem de inversion of control, unde vezi doar o interfata, si nu ce anume se afla in spate? Daca ai, in vre-un fel, de ales (gen “trimite la rezolutia serviciului parametrul isQuebec pe true ca sa primesti o instanta potrivita pentru taxarea din Quebec”), nu crezi ca ai vrea sa fie documentata optiunea asta undeva? Refera-te la prima citare, careia i-ai raspuns cu “what?..” - are mai mult sens acum exemplul meu?

Situatia aceasta am explicat-o folosind metode de programare ce tin, intr-adevar, mai degraba de limbaje compilate, dar acesta e doar din ratiuni de traditie (mostly pentru ca nu prea isi mai bate nimeni capul sa le foloseasca in limbaje scripted). Insa si in limbaje scripted, e la fel de valabil. In JavaScript, o functie poate sa-ti returneze un handle catre o alta functie, care poate sa fie orice functie, inclusiv una care vine in sustinerea exemplului de mai sus cu Quebec-ul. N-ai vrea sa ai aceasta particularitate documentata? Mai ales daca este combinata cu un API de geolocation care iti face selectia automata intre ele bazat pe daca te afli sau nu in Quebec?

Restul ar trebui sa fie evident ca n-are treaba cu calitatea codului, mai ales in coroborare cu ce am raspuns la primele citari.

6 Likes

ca sa fie clar, sunt pentru docblock. sunt impotriva comentariilor din cod care explica logica.

dupa cum am spus, pro docblock.

asta cu pre/post conditii recunosc ca n-o inteleg. daca se asteapta sa se indeplineasca anumite conditii

inainte de folosirea metodei poate ca metoda n-ar trebui facuta publica.

la fel ca mai sus. daca are efecte secundare inseamna ca n-ai voie sa o apelezi public.

nu sunt familiar cu .NET

if platform == linux don't do stuff. //aka return false
si-n dockblock explici ca intoarce false daca-i linux (la @return)

nu ai voie sa ai asa ceva.

Na, despre doc blocks era vorba.

Pre-/Post-conditiile sunt parte din metoda Design by Contract, foarte des folosita cel putin ca principii in software development enterprise.

Legat de daca se asteapta sa se indeplineasca anumite conditii inainte de folosirea metodei, aceasta n-ar trebui sa fie publica… cum exact este o astfel de regula fezabila? Poti sa trimiti date printr-un socket inainte sa faci socket-ul sa asculte sau sa se conecteze? Poti sa incepi o tranzactie spre o baza de date daca nu ai deschis conexiunea la acea baza de date? Poti sa trimiti date pe canalul HTTP daca clientul a inchis conexiunea? Poti sa trimiti date pe canalul HTTPS inainte de handshake? Si pot continua in felul acesta la nesfarsit cu exemple de o banalitate incredibila care demonstreaza ca regula mentionata de tine nu are sens. Conditii trebuie sa fie indeplinite tot timpul pentru ca sa ai un cod care ruleaza fara probleme.

Chiar si daca vorbesti de programare atat de high-level incat practic orice preconditie iti este rezolvata prin insasi definitia mediului de programare/framework-ului, documentatia framework-ului in sine trebuie facuta astfel incat sa iti explice ce se intampla. Poate, in calitate de junior programmer, nu te intereseaza ca o metoda de genul BeginTransaction in spate se ocupa sa iti deschida o conexiune catre un DB, sa iti promoveze tranzactia catre una distribuita, daca e cazul, etc., iar cand termina, sa gestioneze conexiunea si tranzactia, insa ca si developer senior sau arhitect msoftware, aceste mici lucruri conteaza mult.

Aha… siiiiiiiiiiiigur ca da. Ia vezi de tine in:

  1. COM/COM+
  2. GDI/GDI+
  3. DirectX/OpenGL/Vulkan
  4. Orice are de-a face cu controlul surselor de alimentare
  5. Automotive HW
  6. HW diagnostics
  7. Medical HW
  8. Sisteme de business workflow management
  9. Aproape toate limbajele/framework-urile de prototyping
  10. Low-level OS frameworks
  11. Operating systems

…si pot continua, dar cred ca ajunge.

Nu poti sa impui o astfel de regula intr-o aplicatie altfel decat o amarata de pagina web. Orice ai vrea mai complex decat information I/O de la user, trebuie sa tii cont de efecte secundare:

  • Vrei sa citesti/scrii fisiere? Poti, dar doar cu suport de la OS, iar daca nu joci dupa regulile OS-ului, acesta te arde; incearca sa nu inchizi un fisier, de exemplu, dupa ce l-ai scris, ca un experiment simplu
  • Vrei sa faci grafica 3D? Sigur, dar trebuie sa tii cont de design-ul framework-ului ales si de particularitatile placii video si a driver-ului
  • Vrei sa comunici in retea? Sigur ca da, insa socket-urile sunt OS-level, nu le faci/desfaci tu cand/cum vrei
  • Vrei sa scrii tranzactional intr-o baza de date distribuita logic si geografic? Apai, pune frumos mana pe o Biblie (substituie cu Qu’ran sau cartea de capatai a religiei ce se potriveste, sau orice altceva, dupa caz), fa-ti o cruce (sau plecaciune, sau ce s-o fi potrivind) si roaga-te la tot dumnezeul/toti zeii/monstrul zburator din spaghete/orice altceva aplicabil ca toate celelalte 5zeci si ceva de sisteme din spate, pe care nu le observi dar de care depinzi, sa functioneze toate ca o masinarie cu design german, fabricata in Japonia, bine unsa in Romania catre muncitori chinezi membri de partid, ca daca nu, tot in fata debugger-ului te gasim si ras-poimaine.

Si asa mai departe.

Si da, toate metodele care fac asta sunt publice, caci, surpriza… trebuie sa putem sa facem toate cele descrise mai sus.

Eram foarte tentat sa scriu “Aparent nici cu Java, C/C++, Go, Python…” :wink:

Acestea sunt concepte aplicabile cam la toate limbajele de programare, foarte des intalnite, si pe care exista atat discutii lungi si laborioase, cat si tehnici de optimizare care intra atat de adanc de-ti sta mintea in loc. Bug-uri, tipologii de defecte si metode de profiling au fost documentate in 'jde mii de pagini.

Ma rog, toate limbajele exceptand PHP aparent…

Cred ca bagatelizezi putin prea tare o problema foarte reala.

La aceasta pagina sunt prezentate diferente intre engine-urile din browsere la suport HTML. Desi astazi ne aflam in situatia fericita in care pagina este aproape in totalitate verde, nu a fost intotdeauna asa. Cei care au facut pagini web care aveau nevoie de suport Internet Explorer 6 stiu despre ce vorbesc, si cred ca ultima dat cand am facut cu 2 prieteni un maraton de “HTML compatibility horror stories”, a trebuit sa facem la un moment dat pauza sa mergem sa cumparam si a 4-a lada de beri.

Thankfully, standardele si compatibilitatea au ajuns la un stadiu mult mai avansat de atat. Insa nu e greu sa extrapolezi la ce se intampla astazi: framework-uri cross-platform, cross-device, containere de abstractizare a hardware-ului, containere de abstractizare a sistemului de operare, etc. Toate aceste le programaza cineva, si da, diferentele intre platforme exista, sunt reale, si sunt de regula urate.

Serios? Dar cine zice asta? Este acet principiu testabil, respectabil si aplicabilt fara comeentarii? Eu cred ca nu.

Apropos, exemplul cu Quebec era dat dintr-o aplicatie reala la care am lucrat. Si s-a implementat in acest fel deoarece trebuia implementata legislatie noua. Acea implementare avea un termen de 2 luni pana la release-ul in productie, pentru ca atunci intra in vigoare noua legislatie, si clientii nu puteau ramane cu ochii-n soare.

S-a facut o estimare a efortului pentru o solutie “corecta”, si ne-a iesit ca vom avea un roll-out la 3 luni. Si s-a facut si o estimare pe o solutie “hacky”, care continea si metoda descrisa mai sus, si care putea ajunge in productie in 3 saptamani ca sa poata fi testata si de utilizatori. Chici care a fost aleasa?

8 luni mai tarziu, era implementata solutia “corecta”, iar metoda a disparut. Insa timp de 4 luni, cea “hacky” a fost acolo, si a fost documentata. Si inca 4 luni in care a ramas tot acolo, ruland in paralel cu cea “corecta”, deoarece daca schimbi un algoritm agreat si testat, trebuie sa ii validezi si datele.

Comentarii sweep-all de genul “n-ai voie cu asa ceva” nu sunt in vre-un fel productive. Activitatea de development este complexa, dificila, creativa si cu foarte multe provocari intelectuale. Doar pentru ca o solutie nu respecta ad-litteram un principiu/o regula/o conventie nu inseamna ca este de calitate slaba. Inseamna doar ca, in acel moment, in acele conditii, a fost acea metoda care a fost aleasa ca treaba sa fie facuta, preferabil cu un impact cat mai pozitiv acum si cat mai putin negativ in viitor.

Un software bun evolueaza in timp, iar codebase-ul sufera modificari si el. Technical debt se aduna, se scade, solutiile sunt mai aproape de principii sau mai aproape de practica, sau oriunde intre cele doua. Nu mai tin minte cine spunea “Don’t let procedure get in the way of productivity” - cred ca se aplica. Iar codul “de calitate” nu e intotdeauna cel scris conform cu toate regulile si principiile enuntate in software development.

3 Likes

De obicei, daca in cod am adaugat un algoritm mai complicat las o mica descriere. Ce face algoritmul si ideea generala. De asemenea, la inceputul fisierului adaug email-ul meu, departamentul si reviziile. Prin revizii ma refer la modificari asupra codului. Stiu ca si Git ar fi bun pt acest lucru.

In general comentez lucrurile care nu sunt evidente.

@adimosh ai dat raspunsuri foarte interesante (:+1: ) Ca o paranteza eu mai folosesc ILSpy pt a decompila dll-urile .net

Voi cum va comentati codul ? :slight_smile:


Mi se pare redundant acest comentariu.

Eu simt nevoia compulsivă de a pune comentarii aparent redundante mai ales când folosesc un framework cu care sunt mai puţin familiarizat, de exemplu cum e bucata de mai jos:

int analyze_content(struct in_addr in_addr, GByteArray *buf, char *msgout, size_t msglen)
{
    GMimeStream *stream = g_mime_stream_mem_new_with_byte_array(buf);

    GMimeParser *parser = g_mime_parser_new_with_stream(stream);
    g_object_unref(stream); // parser has its own ref

    GMimeMessage *message = g_mime_parser_construct_message(parser);
    g_object_unref(parser); // message has its own ref

    log_info(message);

    result_t result;
    result.in_addr = in_addr;
    result.result = 0;
    result.msgout = msgout;
    result.msglen = msglen;

    // inspect header
    GMimeHeaderList *header_list = g_mime_object_get_header_list((GMimeObject*)message);
    analyze_header(header_list, &result);

    // parse parts
    g_mime_message_foreach(message, mime_message_foreach_callback, &result);

    // cleanup
    g_object_unref(message);

    return result.result;
}

Mai ales în C, niciodată nu e o idee rea să comentezi “nu uita sa eliberezi memoria alocată”. Sau invers: “atenţie la double-free”.

3 Likes

@serghei alea nu sunt docblocks :smiley:

Right, am deviat în comentarii din cod. Credeam că nici cu astea nu eşti de acord :slight_smile:

Păi împotriva lor sunt și mai înverșunat :smiley:

Comentariile inline sunt și mai dispuse la a fi expirate. În plus, de cele mai multe ori poți scrie codul suficient de explicit pentru a nu fi nevoie de ele :slight_smile:

Am scris raspunsuri care au parut interesante, dar nu am dat nici un exemplu. Asa ca voi da 3. Toate sunt in formatul de XML comments, dar sper ca se inteleg si pentru cei ce folosesc alte formate:

Exemplul 1:

    /// <summary>
    /// Gets the count of unused objects in the pool.
    /// </summary>
    /// <value>The currently-unused object count.</value>
    /// <remarks>
    /// <para>If this property returns 0, there are no more unused objects in the pool. If one is requested, a new one will most likely be created.</para>
    /// <para>You should not make decisions on new instances based on this value, as it is not synchronized with the actual pool. It is possible to get a value of 0 here and
    /// a new item to not be created, should an object be returned to the pool between the property being accessed and the <see cref="Get"/> method called.</para>
    /// </remarks>
    public int Count => ...;

Aceasta proprietate a fost folosita in a determina activitatea unei aplicatii (gen objectPool.Count < Constants.MaxObjectsInPool -> application is probably busy). Comentariul serveste pentru a avertiza developerii ca numarul nu este sincronizat cu colectia, prin urmare ca nu ar trebui sa se ia decizii pe baza acestei valori. Inainte sa existe acest comentariu, in aplicatie exista un bug, o situatie in care proprietatea arata o valoare, dar colectia din spate deja avea valoarea schimbata. Comentariul, daca ar fi fost pus cand trebuia, ar fi salvat developerii de acest bug. Aici am exemplificat punctul #3 din raspunsul meu initial.

Exemplul 2:

    /// <summary>
    /// Gets a value from the dictionary, optionally generating one if the key is not found.
    /// </summary>
    /// <typeparam name="TParam1">The type of parameter to be passed to the invoked method at index 0.</typeparam>
    /// <param name="key">The key.</param>
    /// <param name="valueGenerator">The value generator.</param>
    /// <param name="param1">A parameter of type <typeparamref name="TParam1" /> to pass to the invoked method at index 0.</param>
    /// <returns>The value corresponding to the key, that is guaranteed to exist in the dictionary after this method.</returns>
    /// <remarks>
    /// <para>The <paramref name="valueGenerator" /> method is guaranteed to not be invoked if the key exists.</para>
    /// <para>When the <paramref name="valueGenerator" /> method is invoked, it will be invoked within the write lock. Please ensure that no member of the dictionary is called within it.</para>
    /// </remarks>
    public TValue GetOrAdd<TParam1>(TKey key, Func<TParam1, TValue> valueGenerator, TParam1 param1)
    { ... }

Al doilea paragraf din sectiunea de este cheia intregului exemplu: a avertiza developer-ul ca un cod se va executa in interiorul unui lock. Si, pentru a preveni probleme de lock reentrancy, acel paragraf a fost pus acolo. Acest exemplu este unul platform-specific unde develoer-ul este avertizat sa nu faca ceva ce ii este permis de platforma, dar nu este dezirabil in acest caz, deci punctul #6 din raspunsul meu.

Exemplul 3:

    /// <summary>
    /// Has the last operation performed on the item undone.
    /// </summary>
    /// <remarks><para>If the object is captured, the method will call the capturing parent's Undo method, which can bubble down to
    /// the last instance of an undo-/redo-capable object.</para>
    /// <para>If that is the case, the capturing object is solely responsible for ensuring that the inner state of the whole
    /// system is correct. Implementing classes should not expect this method to also handle state.</para>
    /// <para>If the object is released, it is expected that this method once again starts ensuring state when called.</para></remarks>
    public void Undo()
    { ... }

In acest caz, comentariul despre state handling necesita intelegerea sistemului intreg pentru a nu fi necesar, lucru care nu este neaparat nici de dorit, nici nu poate fi impus programatorilor ce folosesc acest cod. Insa acest docblock a explicat tot ce este nevoie pentru a folosi metoda corect. Cred ca acest ultim exemplu poate fi aplicabil, ca si concept, si pentru limbaje scripted. Exemplifica punctul #8.

Sper ca ajuta.

Personal, eu nu sustin idei de genul “Code should be self-documenting” sau “We don’t need no stinkin’ comments”. O asemenea atitudine functioneaza atata timp cat codul este sau foarte simplu, sau foarte liniar. Insa, in aplicatii serioase, in sisteme de operare, driveri sau in sisteme distribuite, rareori este cazul. Si atunci, un docblock pus unde trebuie salveaza multe ore de sapat prin cod.

1 Like