Atenţie! Aceasta este o versiune veche a paginii, scrisă la 2007-10-27 14:20:04.
Revizia anterioară   Revizia următoare  

Infoarena3

Codul din spatele site-ului infoarena2 este intr-o stare foarte proasta si trebuie rescris. Asta este o propunere foarte ambitioasa si riscanta si este nevoie de o explicatie detaliata pentru a demonstra ca rescrierea soft-ului din spatelei site-ul de la 0 este cea mai buna modalitate de a avansa site-ul.

Putina istorie

Site-ul infoarena1 a fost scris acum vreo 4-5 de Cristi pentru a fi prezentat la infoeducatie, un concurs de soft de la Galaciuc. Site-ul era foarte impresionant si a castigat concursul chiar timp de 2 ani la rand. Mai mult, site-ul era atat de bun incat a intrat in "productie" si a adunat o comunitate in jurul lui care a produs un numar impresionant de probleme si concursuri.

Soft-ul din spatele site-ului a ramas aproape identic cu ce a venit Cristi la Galaciuc; s-au facut doar niste schimbari absolut necesare si bug-fix-uri. Am incercat sa punem pe picioare site-ul trac, sa bagam codul in subversion dar in realitate nimeni nu s-a atins de cod. Au crescut niste lucruri pe langa cod; cum ar fi un "portal editabil" de informatica si o integrare foarte neplacuta cu soft-ul media-wiki. Eventual am ajuns la concluzia ca nu se poate face nimic si trebuie rescris de la 0.

In vara lui 2006 ne-am adunat 5-6 flacai, ne-am dus in munti si ne-am incuiat in casa ca sa facem infoarena2 (multumim din nou lui vali). Intre noi il aveam doar pe Cristi cu o oarecare experienta in web-development, in rest eram in cea mai mare parte incepatori in php, mysql, etc. Site-ul nu a fost terminat acolo in munti si a fost nevoie de un numar de "coding camp"-uri pentru a pune site-ul pe picioare si a ajunge macar la nivelul de functionalitate din infoarena1. Site-ul a fost eventual lansat spre sfarsitul lui 2006, dar parti de genul editarea de runde si probleme (o cerinta critica) a fost finalizata doar in 2007.

Softul infoarena2 este mult mult mai bun decat cel din infoarena1. Acest lucru se poate vedea cel mai bine din calitatea si dimensiunile site-ului. Este suficient doar ca numarul de probleme din arhiva este mai mult decat dublu, dar acum este posibil pentru comunitatea site-ului sa ne ajute mult mai mult.

Din pacate dezvoltarea la site-ul infoarena2 a stagnat de multa vreme. Au mai fost tentative de coding-camp-uri si chiar s-au adaugat feature-uri noi, dar progresul este mult prea incet. Exista multe cauze pentru aceasta stagnare, dar eu (Leonard) consider ca problema este este calitatea codului. Infoarena2 este un soft complicat si incurcat in care este mult prea dificil sa faci modificari, iar asta scade motivatia developerilor.

Putem sa incercam niste "boost-uri" de motivare prin coding-camp-uri dar scopul este sa avem un grup de indivizi care lucreaza de acasa in timpul lor liber. Pentru asta trebuie ca programarea sa fie usoara si distractiva, iar in infoarena2 nu este cazul. Soft-ul nostru este mult mai "frumos" decat multe produse comerciale, dar la infoarena2 nu exista motivatia financiara (si nici nu vrem sa existe).

Problemele infoarena2

Pe parcursul dezvoltarii infoarena2 noi (Cristi, Leonard, Mircea, Vali, etc...) am facut un numar de greseli majore la care acum simtim efectele. Daca incepem din nou programarea la proiectul infoarena3 nu o sa facem din nou aceleasi greseli si site-ul va fi mult mai bun. Daca stim ce am gresit si cum sa evitam nu vom ajunge din nou in aceasi situatie.

Poate parea trist ca aruncam la gunoi aproape un an de efort, dar nu este cazul. Vom pastra tot continului site-ului, care valoreaza enorm (si asta tine de fapt infoarena.ro in viata). Si vom pastra lectiile infoarena2, care sunt mult mai valoreasa decat codul efectiv scris in php. Ar fi cu totul altceva daca alti oameni ar rescrie codul, ei probabil ca ar face aceleasi prostii si ar fi mai bine sa se tina de treaba la infoarean2.

Am facut o lista cu ce anume am gresit in infoarena2 si cum putem face mai bine (in infoarena3). Este posibil sa reparam multe dintre probleme in infoarena2, fara o rescriere, dar multe dintre aceste greseli gresite vizeaza arhitectura fundamentala a site-ului. Acestea nu pot fi reparate decat printr-un efort enorm, iar acel efort cumulat ar fi mai mare decat rescrierea de la 0.

ia_parameter_values

Unul dintre tintele infoarena2 a fost sa avem mai multe tipuri de runde si probleme. Fiecare tip de runda sau de problema are alti "parametri", ne-am gandit sa tinem toti acei parametri intr-un sigur tabel de forma "id-obiect", "nume-parametru", "valoare". Este o idee foarte proasta care nu are absolut nici un merit.

Ar trebui sa avem pentru fiecare tip de problema sau runda un tabel de genul ia_classic_task, care contine o coloana task_id si apoi cate o coloana pentru fiecare parametru. Eu (Leonard) am incercat aceasta transformare dar nu am reusit (din cauza repercursiuni in restul site-ului) si am renuntat.

ia_score

Tabelul ia_score are coloanele: score_id, user_id, task_id, round_id si score. Primele 4 coloane sunt sunt nulabile, asa ca tabelul nu are PK. Idea era sa tinem scoruri per runda cu task_id NULL si eventual statistici per task/round cu user_id NULL. Este un tabel oribil si mi-e rusine de ce am facut acolo.

Este prea greu sa faci query-uri in tabel si din pacate infoarena1 avea de fapt mai multe statistici decat infoarena2. Asta cred ca este singurul punct in care infoarena1 depaseste infoarena2.

Acest tabel ar trebui spart in mai multe tabele fara coloane nulabile. Idea fundamental gresita de aici este economisirea numarului de tabele din baza de date, care este o prostie. Este ca si cum ai incerca sa folosesi mai putine functii facand copy-paste.

Id-uri VARCHAR

Id-urile pentru utilizatori sunt numere, dar restul sunt VARCHAR cu niste validari facuta in cod prin regex-uri. Ar fi mai bine sa avem task_id ca numar si task_name ca string absolut peste tot. task_name se poate obtine foarte usor din task_id adaugand un join trivial.

MySQL nu face index pe hash-uri DOC LINK HERE pentru tabele pe disc, el sorteaza id-urile tinand cont de colatii (latin2). Tinand string-uri peste tot crestem dimensiunile tabelelor, si asta este oribil pentru tabele de genul ia_score sau ia_job. S-ar merita de facut niste teste de performanta comparand un tabel de scor exclusiv numeric cu unul plin de string-uri.

Securitatea, si magia din wiki

Pe parcursul dezvoltarii infoarena2 ne-am dorit sa evitam pe cat posibil functionalitatea magica din wiki, si am mers prea departe. Am pornit de la idea ca orice pagina este o pagina de wiki, si paginile de runde sau probleme sunt doar un caz oarecare de pagina wiki. Securitatea paginilor de utilizatori si de probleme este totusi subordonata problemelor, si asta am realizat adaugand un "descriptor de securitate" ca string pentru fiecare pagina de wiki. Pagina problema/adunare are la securitate un string "task: adunare", asa ca vizibilitatea depinde de vizibilitatea task-ului. Este un sistem prea generic, incurcat si greu de folosit sau extins.

Ar fi mai ok ca orice url de format problema/xxx sa intre prin controller-ul de task-uri, care subordoneaza textblock-urile care incep cu problema/xxx. Similar am avea controllere de news, blog, user page care subordoneaza tot ce incepe cu news, blog sau user/costel. Pentru restul paginilor am avea un controler DISTINCT de wiki. Codul pentru bucatile de editare si istorie poate fi refolosit in 1000 de moduri, dar nu este acceptabil sa nu poti ajunge de la editarea de enunt la editarea de problema fara sa modifici in address bar.

Nu este nimic gresit in a avea tabele ia_news, ia_blog_post si ia_wiki care sunt "deasupra" lui ia_textblock, iar ia_textblock sa fie folosit doar pentru versionarea unor bucati mari de text. Securitatea private/protected/public (care este FOARTE utila si absolut OK) poate fi un simplu enum in ia_wiki.

Efortul necesar pentru o astfel de transformare in infoarena2 mi se pare absolut enorm.

Layer de logica

Infoarena2 pretinde ca foloseste o arhitectura MVC, dar MVC este o notiunea foarte larga si vag definita. Nu este interesanta o discutie detaliata asupra ce inseamna MVC, asa ca voi discuta doar ce se foloseste efectiv in site-ul infoarena2.

Url-urile sunt parsate in index.php si in functie de o logic complicata si nu foarte interesanta fiecare request http este pasat la un "controller", care este in principiu o functie php de prin www/controllers. Acel controller face ceva cu requestul, de obicei baga niste request-uri in baza, si apoi face un hash de "date pentru afisat" care il trimite la un view.

View-urile nu sunt functii, sunt fisiere .php oarecare din www/views. Lansarea unui view este o operatie sinucigasa, se executa fisierul de view folosind hash-ul de date si se trimite direct pe "teava". In acel view este posibil sa se foloseasca textile, care poate executa macro-uri care(de obicei) se duc pana in baza. Asta inseamna ca poti sa te duci in baza dupa executia controller-ului, dar nu mi se pare nimic rau in asta. Macro-urile sunt efectiv niste mini-controllere.

Problema este ca noi din controllere ne ducem direct in baza si logica fragila de genul securitate este imprastiata si combinata cu cod jegos de parsat/validat request-ul (in controller) sau construit query-uri sql (in functiile de db). Aceasta problema are o rezolvare destul de clara si bine-cunoscuta (dar nu de catre noi in vara 2006).

Intre codul de controller(UI) si codul de baza de data(DB) sa mai pune niste cod de "business logic"(BL). Tot ce inseamna parsarea request-ului se face in UI, tot ce inseamna contruirea de SQL se face in DB. BL contine de fapt tot codul cu adevarat interesant pentru functionarea corecta a site-ul. Codul de DB nu trebuie sa aiba grija decat sa construiasca query-uri (si sa evite sql injection) iar codul de UI se ocupa de a vedea ce butoane a apasat utilizatorul.

Unul dintre avantajele majore este ca se pot scrie usor teste pentru BL, iar daca BL-ul nu are greseli atunci UI-ul nu poate sa strice nimic (decat experienta utilizatorului). A se vedea punctul urmator.

Testele pe baza de curl

PHP este un limbaj foarte fragil, unde este foarte usor sa faci greseli grosolane. Asta este o problema generica a limbajelor dinamice de genul php, python, ruby, javascript fata de limbajele de genul C, C++, C# si java, si in PHP este mult mai important sa testezi codul.

Teste curente sunt facute pe baza de curl, curl fiind o librarie de access http. Testele construiesc un request complet http pe care il trimit pe fir, asteapta ca apache sa raspunde si apoi verifica niste chestii din request (in cea mai mare parte prin preg_match si strstr). Ambele faze sunt greu de facut, dar are ca avantaj ca se trece prin tot codul si este foarte "realist".

Pentru a usura testarea am facut controllerele noastre de editare sa accepte un parametru de form absent drept "nu vreau sa editez acest parametru". Asta este necesar in teste dar e un comportament artifical care a "nascut" niste bug-uri foarte urate. In retrospectiva a fost o idee proasta, care a introdus o cuplare oribila intre controllere si codul de test. Ambele sunt acum foarte greu de modificat.

Ar fi mai bine sa testam functii BL care nu au nici o treaba cu HTTP. Daca BL-ul este ok atunci sigur bug-urile din UI nu pot sa strice nimic in baza. UI-ul se poate testa apoi de mana.

ia_textblock, ia_textblock_history

ia_file

Caching exagerat

Un plan de atac

Branching

Nu am folosit eficient branch-uri. Absolut toate modificarile le-am facut direct in trunk iar asta nu este o idee buna. Exista mereu schimbari mari, care au nevoie de mai multe commit-uri pentru a fi complet functionale, iar Astfel de schimbari trebuie facute intai intr-un branch special si abia apoi copiate in trunk.

In asa fel trunk-ul devine mult mai stabil si nu este nimic rau in a abandona o idee. In trunk-ul infoarena2 avem cod mort pentru idei abandonate, care acum este dificil de extras. Branch-uri ar fi trebuit sa facem pentru cache-ing, editoare de probleme, validate_array, tag-uri, blog, dataset-uri etc.

LINK TO PRODUCING OSS

Responsabilitati

Codul infoarena este open-source, iar oamenii lucreaza doar in timpul lor liber. Dar daca vrei sa ajuti infoarena atunci trebuie sa te tii de treaba si rezolvi tichetele pana la o anumita data. Ar ajuta mult ca un tichet "acceptat" sa aiba un owner care este trebuie sa rezolve acel tichet pana la o anumita data.

Bineinteles ca nu avem cum sa fortam pe nimeni sa lucreze la infoarena, dar se presupune ca un individ care vrea sa ne ajute cu adevarat nu vrea sa frece menta. Astfel de dead-line-uri ar fi un impuls interior pentru fiecare din noi. Atentie: nu ma refer la dead-line-uri de genul "ar fi frumos sa avem #123 rezolvat pana luna viitoare", ma refer la lucruri de genul "eu rezolv #124 pana maine si #125 poimaine".

Ordine in tichete

Demo-uri

  • Framework-uri
  • SqlAlchemy
  • Selenium
  • Form-uri
  • Tabele
  • BL prin HTTP
  • Wiki si atasamente in subversion.

Arest la domiciliu