Souboj frameworků nevyhrál framework
Školení, která pořádám
Možná jste zachytili, že Jirka Knesl pořádal souboj frameworků a že výsledky prezentoval na WebExpu. Tohoto souboje jsem se také zúčastnil, pro zábavu úplně bez frameworku. Chtěl jsem zjistit, kde mi bude framework nejvíc chybět a na jakých částech kódu to bude nejvíc znát.
V souboji jsem vyřešil nejvíc úloh a obsadil jsem první příčku. Jirka to vyhodnotil takhle:
Pokud můžete najmout Jakuba Vránu, udělejte to. Pokud nemůžete najmout Jakuba Vránu, vyberte si Nette, Rails nebo Symfony a pořádně se ho naučte.
Asi to myslel trochu v nadsázce, i tak je mi z toho ale poněkud smutno. Můj pohled je totiž dost odlišný: Při vývoji aplikace jde totiž podle mě hlavně o to vyjádřit nějakou myšlenku, tedy napsat kód, který je uvnitř metod. Jak jsou tyto metody propojeny považuji až za druhotné. Strávit na tom 10 % času je v pořádku, ale mám pocit, že řada lidí se v tom babrá zbytečně moc. Stráví tím většinu času, pak konečně něco napíšou, přičemž zjistí, že to musí celé předělat. Pokud to má někdo v paži a rovnou vyrábí dokonale navržené aplikace, tak je to fajn, ale spíše než hledáním této dokonalosti je podle mě lepší trávit čas výrobou nedokonalých aplikací.
Dále je potřeba říct, že na mém vítězství se výrazně podepsalo použití Adminer Editoru pro vytvoření administrace. Některé frameworky podporují vygenerování administračního rozhraní (což nesnáším, protože při jakékoliv změně je potřeba ho přegenerovat), s jinými lze administraci naprogramovat, oboje je ale dost práce. Adminer Editor je aplikace, která vám dá administrační rozhraní zadarmo. Pokud je potřeba ho upravit, tak lze pro něj vytvořit přizpůsobení. Pokud frameworky nabízejí abstrakci pro běžné obraty, tak tohle je ještě o úroveň dál.
Chyby
Jirkovi se mé řešení líbilo ze všech nejmíň, protože „používalo objekty jen v méně než 10 % kódu“. Co považuji za nejhorší místa kódu já?
- Routování v
.htaccess
je odporné. Udělal jsem ho proto, že jsem čekal nějaké požadavky na tvar URL. Vzhledem k tomu, že žádné nebyly, tak bych celou tuhle část nejspíš vyhodil.
- V souboru s funkcemi se míchají znovupoužitelné funkce s těmi specifickými pro tento projekt.
- Kontrolu proti CSRF je potřeba volat ručně, takže se na ni dá zapomenout.
- Ošetření proti XSS je potřeba volat ručně, takže na něj lze zapomenout.
- Obrana proti SQL Injection je v prezentaci centrální, ale v přizpůsobení administrace se dělá ručně, takže na ni jde zapomenout.
- URL jsou rozesetá po celé aplikaci, takže jejich změna by byla bolestná. Nicméně vzhledem k tomu, že URL je nejlepší stejně nikdy neměnit, tak to za moc velkou chybu nepovažuji.
- Musel jsem myslet na to, aby se v cyklech opakovaně nepokládaly dotazy do databáze a vyčlenit je před cyklus. Když bych to neudělal, kód by byl trochu jednodušší, ale taky pomalejší.
- Asi největší prasárna je přidání sloupce do správy produktů v administraci. To je udělané vložením
<td>
do hodnoty (kterou Adminer ošetří) a jejím následným odošetřením. Je to hack, který jsem udělal jen proto, abych měl úlohu rychle hotovou, ve skutečném projektu bych na to do Admineru doplnil abstrakci.
Podotýkám, že bezpečnostní a výkonnostní chyby jsou potenciální, nikoliv aktuální – když bych si nedal pozor, tak by je aplikace klidně mohla mít.
Framework je pro mě nejužitečnější tím, že mi dovoluje správné věci dělat snadno a neobvyklé složitě. Nette Latte je elegantní šablonovací jazyk, za jehož největší přednost považuji automatickou obranu proti XSS – neošetřený HTML kód se dá vypsat, ale nikoliv opomenutím. NotORM nabízí obranu proti SQL Injection a řešení většiny výkonnostních problémů s přístupem k databázi – napsat neefektivní kód v něm jde, ale dá to dost práce. Nette formuláře nabízí obranu proti CSRF jednou metodou, za ještě užitečnější bych považoval, když by byla u POST formulářů při aktivní session automatická a pokud bych ji nechtěl, tak bych ji musel ručně vypnout. Různé frameworky kladou důraz na různé věci a nejdůležitější je pro mě to, jak pohodlně mi umožňují dělat věci, které považuji za správné.
Úkoly
V zadání jsem čekal několik úkolů, na kterých by se síla frameworků dobře projevila a s kterými bych já bez frameworku měl větší problémy:
- Stránkování v kategoriích.
- Překlad uživatelského rozhraní a produktů.
- Přesměrování starých URL na aktuální.
Závěr
Souboj frameworků v této podobě považuji za promarněnou příležitost. Čekal bych porovnání kódu v jednotlivých oblastech (jak těžké je v kterém frameworku udělat XSS, jaké se položí dotazy do databáze při přímočarém kódu, jak snadné je kód otestovat, …). Dále bych čekal více úloh, s kterými frameworky můžou opravdu pomoci. Místo toho jsem se dozvěděl, že je nejlepší najmout si někoho, kdo rychle píše, a že na výběru frameworku vlastně nezáleží. Já bych po sobě svůj kód přebírat nechtěl – sice v něm momentálně žádné zvenku viditelné chyby nejsou, ale aby se jim zabránilo, tak musí být kód poněkud krkolomný.
Diskuse
Ono se hlavně ukázalo, že frameworky nelze za jeden den smysluplně porovnat. Síla frameworků je podle mě hlavně v tom, že mi dodají nějaký obvyklý a doporučený způsob řešení problémů. To pak vede k tomu, že kód, který používá framework, bude do budoucna lépe udržovatelný a že další programátor z týmu se v něm bude dobře orientovat. Tohle se za jeden den prostě ověřit nedá.
Pokud framework chápu jako nástroj pro minimalizování počtu chyb, tak se dá porovnat aspoň něco:
- Zend Framework je sám o sobě těžko použitelný, protože ubránit se proti XSS je extrémně pracné, v Nette to je naopak výchozí chování.
- V Nette je potřeba myslet na obranu proti CSRF, jiné frameworky to možná nabízí automaticky.
- Doctrine defaultně neefektivně komunikuje s databází, s NotORM je naopak těžké něco udělat špatně.
- V Ruby on Rails je pracné otestovat modely, protože nepoužívají Dependency Injection.
To střílím jen tak od boku, něco možná není zcela přesné. Ale je to ukázka závěrů, které se z toho souboje daly odvodit.
Pro zjištění snadnosti údržby do budoucna se daly připravit úlohy, např. „fajn, už to je celé hotové, jen ještě udělejte anglickou verzi“. Na kolik míst aplikace se bude muset sáhnout, bude stačit změnit pohledy nebo bude potřeba upravit i controllery, jak se změna projeví v modelu (pokud budeme chtít překládat třeba názvy a popisy produktů), co budu muset změnit v routování?
Martin:
napsat "V Ruby on Rails je pracné otestovat
modely, protože nepoužívají Dependency
Injection." je stejné jako "dostat se s vrtulníkem na střechu je obtížné, protože se nevejde na schodiště"
"V Ruby on Rails je pracné otestovat modely, protože nepoužívají Dependency Injection."
Ano, testovalo by sa to lepsie izolovane, keby tam neboli zadratovane zavislosti na logger, connection, configuration ... avsak hovorit, ze je to pracne je trochu zavadzajuce.
Rails ma out-of-the box podporu testovacej databazy a dokonca aj kostru testov pre modely generuje (ked sa generuje model). Cize testovanie by som ani zdaleka nenazval pracnym, naopak... keby som mal nastavovat mocky/stuby pre kazdy model, povazoval by som to za ovela pracnejsie.
Inak suhlasim, to porovnanie mohlo byt aspon na urovni kodu jednotlivych uloh.
Martin Lonský:
Před takovýmto výrokem bych doporučil nastudovat ZF2.
Kaja:
Pokud jsem něco v manuálu nepřehlédnul, tak člověk musí pořad explicitně volat v šabloně
<?php echo $this->escapeHtml($output); ?>, což je asi důvod narážky s XSS.
Kaja:
ZF 1
<?php echo $this->escape($a) ?>
ZF 2
<?php echo $this->escapeHtml($a) ?>
Každopádně je to problém spíš přístupu, že Zend nemá šablonovací systém nebo alespoň out of box podporu pro nějaký existující. V obyčejných PHP šablonách toho člověk víc nevymyslí.
Jakub Vrána :
Ano, tohle považuji za v praxi nepoužitelné. Na zavolání ošetřovací funkce je tak snadné zapomenout, že k tomu nutně dřív nebo později dojde a XSS je na světě.
Tomáš Fejfar:
Správná odpověď je, že záleží na tom, co používáš za templatovací systém - jestli Latte, Moustache, čisté PHP nebo něco jiného ;)
Jakub Vrána :
Proto jsem psal, že je „sám o sobě“ prakticky nepoužitelný. S nějakým šablonovacím systémem budiž, ale se samotným ZF bych aplikaci budovat nechtěl, obzvlášť pokud by v týmu byli lidi, kteří tohle nikdy nemuseli řešit, takže to nemají v krvi (ať už proto, že na obranu proti XSS kašlali, nebo naopak proto, že ji framework dělal za ně).
karmi:
> V Ruby on Rails je pracné otestovat modely, protože nepoužívají Dependency Injection.
WAT?!
Jakub Vrána :
Ha ha, to jsem nevěděl, že mi sem chodí tolik příznivců RoR.
Vopičák:
> Pokud framework chápu jako nástroj pro minimalizování počtu chyb
To je trochu zvrácené chápání frameworku -- jejich smysl je v první řadě úspora práce, znovupoužitelnost.
Jakub Vrána :
To je jinými slovy řečeno totéž. Kopírovat kód z jednoho místa na druhé je taky chyba, které mi framework pomůže zabránit.
Miloslav Ponkrác:
Já jsem hlavně nepochopil co se porovnávalo.
Z pera pana Knesla ani zde jsem nepochopil, jaká je hodnotící funkce, podle které se seřazují „kdo je na 1. místě“, „kdo na 2. místě“, … Nutno říci, že jsem po tom výrazněji nepátral.
Pokud je hodnotící kritérium rychlost, tedy nejkratší čas, pak vyhrává ten, kdo nejvíc prasí a udělá nejrychlejší prototyp. Tedy ten, kdo je schopen nejrychleji udělat prototyp na zahození, jakkoli zprasen a jakkoli rychle. Ale bude to nepoužitelný kód pro údržbu.
Frameworky jsou dělány s tím, že nabízejí možnost udržet a rychle upravovat rozsáhlé weby a funkčnosti. Žádný framework není stavěn pro rychlé prasení jako primární funkci. I když by šlo takový udělat.
Proto mě zajímá hodnotící funkce tohoto souboje. Pokud je to primárně čas, pak je celá soutěž k ničemu a výsledky rovněž.
Navíc při malém počtu účastníků je 99 % výsledků dané znalostmi a zkušenostmi lidí, nikoli schopnostmi frameworku.
Na druhé straně, pokud je hodnotícím kritériem čas, je výsledek (kdyby bylo více soutěžících) určující pro vhodnost daných nástrojů pro prototypování a ukázkových kódů pro prezentaci, což je užitečné vědět. Nikoli však pro opravdové projekty, kde záleží i na jiných věcech, než napsat prototyp.
Jakub Vrána :
Já si vlastně (jako soutěžící) taky nejsem jist, co se přesně porovnávalo. Ale z odstavce Pořadí v článku http://www.knesl.com/articles/view/webexpo-…-frameworku soudím, že to byl počet vyřešených úloh v daném čase – tedy skutečně rychlost.
Ale s tím, že šlo o to, co nejrychleji naprasit prototyp, nesouhlasím. Alespoň já jsem se snažil, aby přinejmenším zvenku nebylo poznat, že to není skutečná aplikace. Tedy žádné bezpečnostní ani výkonnostní chyby, rozumná URL, smysluplné chování. Dalo mi to dost práce, kterou bych s frameworkem ušetřil.
Když bych to hodnotil já a některé řešení by obsahovalo závažnou chybu, tak bych to v hodnocení přinejmenším zmínil (případně ho až diskvalifikoval). Protože tu chybu sice udělal člověk, ale framework mu to umožnil.
I tak ale souhlasím s tím, že výsledky jsou spíše funkcí lidí než frameworků.
Miloslav Ponkrác:
Každopádně gratuluji k výsledku, pane Vráno. Zasloužil jste si ho.
Rychlost nebyla jediným kritétiem. Podíval jsem se na první 2 PDF v zadání a objevují se tam „různé fixly“ a speciální úpravy jako: „Za použití migrací získáváte bonus - 30 minut, který vám bude na konci souboje odečten.“
Takže je to velmi divoké.
Ornyx:
Je opravdu škoda, že i přes oznámení, že se zůčastní nekdo s frameworkem Django tam nikdo takový nebyl, protože myslím, že by vyhrál ... s frameworkem už pár let pracuju a zrovna CRUD je v něm hračka (ale dynamický, ne staticky fenerovaný) ... ale o soutěži jsem se dozvěděl až tady a navíc se necítím jako někdo, kdo by byl takový guru, aby mohl soutěžit :)
Jiří Knesl:
Vždyť je zadání k dispozici. Tak to můžete o víkendu zkusit. A když to dobře půjde, můžete se pochlubit. A když to tak dobře nepůjde, můžete se pochlubit taky. Dva z nás pěti jsme taky neuměli framework moc dobře a nevadilo to, zasoutěžili jsme si.
Jiří Prokeš:
Omyl. To že jste frameworky neovládali i ve spaní, právě celou "soutěž" pokazilo.
Jinak byste totiž pana Vránu vyklepli, ani by nevěděl jak.
Jiří Knesl:
Kdybych napsal zadání naschvál tak, aby testovalo to, v čem jsou frameworky dobré, tak by to asi byla pravda.
Ale já jsem naschvál psal zadání tak, abych testoval věci, v kterých si frameworky obvykle nabijou hubu.
Miloslav Ponkrác:
No hlavně jste udělal hokus pokus, kdy nikdo neví, co se vlastně zjišťovalo.
Ve světle Vašich řečí, kdy jste měl v jednom Vašem článku plno „rozumů“ o statistice a o vědeckých metodách, kdy jsem Vás upozornil, že o tom vůbec nic evidetně nevíte a nemáte se ztrapňovat – jste mě touto soutěží přesvědčil, že je to ještě horší.
Neudělal jste ani správný závěr, ani jste nenapsal co se bude hodnotit, ani jste nedodal minimální potřebný počet účastníků, aby výsledkem nebyla pouhá náhoda z náhodného generátoru (a to jste přitom v diskusi se mnou měl spoustu řečí, že 144 lidí v mém výzkumu je málo).
Prostě se příště do žádného zkoumání ani statistiky opravdu nepouštějte. Nemáte na to.
Swed:
Kritizovat umí každej, ale něco zajímavého uspořádat málokdo.
Miloslav Ponkrác:
Jestli je někdo hyperkritický, pak je to pan Knesl. Stačí si číst jeho web. Jednu dobu pouze napadal kde co bez toho, aniž by o věcech něco věděl.
Jiří Knesl:
Jakube, je vidět, že si na přednášce nebyl (pak si ji pusť), stejně jako jiní, kteří kritizují.
Prvních 5 minut bylo: byly tyto frameworky, nejdřív to měl Jakub Vrána, pak jsme byli my ostatní.
Dalších 25 minut jsem vysvětloval to velké "Ale..." a mluvil jsem o různých vlastnostech různých frameworků. Rozebíral jsem formuláře. Rozebíral jsem modely. Rozebíral jsem validace. Rozebíral jsem administrace.
Je škoda, že lidi si vzali za své jen to pořadí a přitom 80 procent přednášky bylo o něčem úplně jiném.
Možná je to dnešní dobou. Pořadí se dá nacpat do 1 tweetu, zatímco rozvahu, jestli je lepší dávat validace do modelu, formuláře, POSTu, kterou jsem strávil snad víc času, než sdělením pořadí, to netweetl nikdo.
Miloslav Ponkrác:
Pokud to lidi nepochopili, je to vina hlavně Vašich nedostatečných vyjadřovacích schopností.
Jiří Knesl:
No přednáška probíhala stylem:
1) nebudu zdržovat a řeknu vám výsledky
2) pak přišlo hlasování a pořadí
3) teď je to velké Ale... tedy ono se výkonnost programátorů ve frameworcích až tak moc měřit nedá, zato se můžem kouknout, jak vypadají různé přístupy
4) 5) 6) ... jednotlivé části frameworků.
Olda:
Pro mne nejzajímavější přednáška z celého webexpa. Díky Jirko za ni. = Postřehy z praxe, porovnání různých metod, tipy. Kdyby takto vypadaly i ostatní.
Swed:
Kde se dá najít video z přednášky?
Jakub Vrána :
Někde jsem četl, že video z přednášky by mělo být k dispozici asi do měsíce, ale dost možná jen pro účastníky konference.
<Script>alert('a');</script>:
<Script>alert('a');</script>
uni:
Sorry, ale musel som si to skúsiť keď tam tak pekne píšete o tých kontrolách :)
mishak:
Trochu mi tu chybí tlačítko to je ale blbec.
msx:
Nechcem vŕtať, ale keď súboj frameworkov, tak mal vyhrať framework. Ale to samozrejme iba v prípade, že v pravidlách bol zadefinovaný framework ako nutnosť (nečítal som pravidlá). Ďalšia vec, že ak správne chápem tomu, čo som očami preletel, tak ani nezáležalo na tom, ako dobre sa robilo v ktorom frameworku, ale na tom, aký bol kvalitný výsledný kód a koľko úloh bolo spracovaných. Takže v podstate to bol len súboj PHP programátorov. Ale samozrejme k tomu prvému miestu gratulujem.
msx:
Zaujímalo by ma, načo je v podstate ošetrenie vstupu pri XSS, pokiaľ sa bude pri vypisovaní vždy používať htmlspecialchars() a pri zápise do databázy mysql_real_escape_string(). V jednom frameworku som si totiž všimol to, že funkcia proti XSS je extrémne zložitá a pre každý vstup sa jedná o určité spomalenie kódu (hlavne pokiaľ je vstup dlhý a je ich veľa) a tiež je teoreticky schopná vyhodiť z reťazca potenciálne neškodné postupnosti znakov (kvôli tomu, že v spojení s inou tiež neškodnou postupnosťou môže dôjsť k XSS). Lebo čisto teoreticky, tak ako sa dá zabudnúť na htmlspecialchars a mysql_real_escape_string, tak sa dá zabudnúť aj na ošetrenie vstupu proti XSS.
Tomáš Fejfar:
Problém ochrany proti XSS je v tom, že záleží na kontextu. Jinak se escapuje JS a jinak HTML a jinak HTML atribut. A zajímavé to začne být, když v JS vypisuješ HTML - v tu chvíli musíš oescapovat nejprve HTML a potom ještě JS. Takže pokud do JS vypíšeš něco oescapované pomocí htmlspecialchars, tak to neznamená, že jsi v bezpečí před XSS :)
msx:
Pravdu vravíš, ale na Javascript je pre zmenu json_encode(). Ale na druhej strane, aby v texte príspevku svietilo <script> a podobné prasačiny, tak tam si myslím ošetrenie XSS má význam. Prípadne aj vtedy, pokiaľ sa má niekde vložiť text priamo (napr. diskusie povoľujúce html elementy a podobne). Neviem, či to aj tu tak nie je. Skúsim <b>tučný</b> text.
Jakub Vrána :
Na vstupu je potřeba zkontrolovat (validovat), jestli vstup vyhovuje požadavkům, které na něj mám. Pokud např. očekáváme čistý text (jako v této diskusi), tak je potřeba zkontrolovat, jestli vyhovuje použitému kódování (typicky UTF-8). Pokud na vstupu očekáváme HTML s podmnožinou značek a atributů a ošetřenými speciálními znaky, tak je potřeba zkontrolovat to.
Na vstupu nelze předjímat, co pak s daty uděláme, v jakém kontextu je použijeme. Můžeme je chtít vypsat do HTML stránky, můžeme je chtít poslat v textovém e-mailu, můžeme je poslat jako odpověď na AJAXový požadavek, můžeme s nimi udělat cokoliv dalšího.
Na výstupu je potřeba převést data z formátu, ve kterém je máme uložena, do formátu, kam je posíláme. Takže při převodu z čistého textu do HTML zavoláme htmlspecialchars(), při použití v JavaScriptu json_encode().
Předjímat na vstupu, co budeme s daty dělat na výstupu, je obvyklá začátečnická chyba, které je dobré se vyvarovat.
Cooler:
Mozna jsem pomalejsi, ale spravna ochrana proti XSS je pri READ nebo WRITE operaci?!? Podle nazoru pana Vrany je to pri read operaci - tedy predpoklada, ze vypisovana data jsou potencialnim zdrojem XSS.
Pominu echo $GET['name'] nebo cookie, to uz je programator retardovanej :) Ale normalne ukladam neco do DB, tak nejdrive zvaliduju, ne? Davam do query WHERE podle GET parametru pres PDO, tak tezko muzu udelat select osoba where id = !5 or 1=1! (XSS mezi vykricniky), protoze me pdo nepusti ne? A to se netyka pouze relacnich db.
Takze je ten Zend fakt tak blbej, nebo od nej je ocekavana blbuvzdornost? Nebo je to nejaka paranoia by design, ze vypisovana data vzdy obsahuji XSS? Chapu, ze ne vzdy se na to da na data v mych DB spolehnout, treba je tam zapisuje nekdo jiny a nemam to pod kontrolou, ale delat z toho obecne pravidlo, ze kazde promenne je potencialni XSS? No nevim.
Jakub Vrána :
Na každé úrovni je potřeba respektovat vnitřní typ dat a ošetřit ho podle toho, kam ho posíláme. Vezměte si třeba takový název firmy. To je čistý text prakticky bez jakýchkoliv omezení (v platném kódování). Může obsahovat třeba apostrof (McDonald's), ampersand (Me & son), klidně jakékoliv další znaky, které mají v různých kontextech různý význam. Když je posíláme do databáze, musíme ošetřit znaky se zvláštním významem v SQL (např. apostrof), když je posíláme do webové stránky, musíme ošetřit znaky, které mají zvláštní význam v HTML (např. ampersand). Ale při ukládání do databáze je nesmysl spekulovat, co s daty budeme dělat, až je z ní budeme zase vytahovat.
U některých dat (např. čísel) nemusíme ošetřovat nic a jen je zkontrolujeme na vstupu. Ale lepší je nad tím moc nepřemýšlet a ošetřit prostě všechno. V Nette Latte klidně napíšu {$id} spíš než {!$id}, i když vím, že v $id bude vždy jen číslo.
Cooler:
Ano, je mi to jasné, není jiné cesty, než escapovat, a to ještě navíc správně (text, atribut "", atribut '', v HTML5 atribut bez uvozovek ... no není to psycho?) :-)
Dnes jsem se procházel na pláži u moře a říkám si kde všude asi tak nemám escapovanou uvozovku... takže během pár hodin je ze mě stejný paranoik. Abychom si pomalu založili skupinku "HTML coding? Paranoia by design!" nebo "Echo Paranoid" ... :-)
Jakub Vrána :
Video už je k dispozici: http://webexpo.cz/praha2012/prednaska/souboj-frameworku/, nicméně jen pro účastníky konference nebo za poplatek.
Srovnání prací bohužel mělo jen dva body:
1. Nekompletní porovnání screenshotů některých administrací.
2. Srovnání validace čísla kreditní karty s validací měsíce pro některé frameworky.
Můj dojem z nevyužité příležitosti se bohužel jen prohloubil.
Diskuse je zrušena z důvodu spamu.