Mijn ontwikkelingen in webontwikkeling

Door Gamebuster op maandag 31 maart 2014 11:44 - Reacties (7)
Categorie: if(post.relatedTo("programming")), Views: 3.448

Al jaren werk ik met Ruby on Rails. Mijn relatie met Ruby on Rails was overweldigend en geweldig. Ruby is een leuke taal en Rails heeft unieke features die mij erg sterk aanspreken. Met geluk en liefde werkte ik met Ruby on Rails.

In mijn vrije tijd speelde ik met andere talen. Zo ben ik al een lange tijd bezig met de ontwikkeling van een kleine game in C en heb ik gespeeld met NodeJS. De ontwikkeling van die projecten verliep soepel. Ik moest veel meer zelf doen, maar ik werd daarbij beloond met uitzonderlijk hoge performance.

Ruby on Rails was niet langer meer de geweldige redder van alles. Ik begon een afkeer te krijgen tegen diverse quirks van Rails. Los daarvan, Ruby en Ruby on Rails zijn samen bijzonder traag. Het is een super framework om wat werk gedaan mee te krijgen, maar als je long-living applicaties hebt waarbij performance een rol speelt, wordt Ruby on Rails niet meer je redder maar dat ding dat je telkens in de weg staat.

"Dit moet anders kunnen" dacht ik bij mezelf. Ik begon te preken bij collega's hoe ik het allemaal wel beter wist en hoe het allemaal anders kon. Ik begon opstandig te worden richting Rails en ik verloor mijn liefde voor haar. Ik begon met het zoeken naar alternatieve manieren voor praktisch alles wat Rails Rails maakt, tot ik me besefte:

Ik wil iets anders. Ik wil niet langer meer moeite doen om haar te veranderen; Ik wil een nieuwe liefde.

Maar wat? Welke omgeving is volwassen genoeg om volledig Ruby on Rails te vervangen?

Ik begon rond te shoppen voor andere frameworks. Gezien mijn positieve ervaringen met C en NodeJS begon ik daarin te zoeken.

Ik wil geen web framework op C++, want dat vind ik een verschrikkelijke taal. C is een leuke taal, maar niet praktisch om een grote web-applicatie mee te maken. NodeJS blijft dan over.

NodeJS heeft al enkele keren in de afgelopen jaren mijn aandacht getrokken. Het heeft een enorme community, Javascript heb ik nooit een afkeur tegen gehad en NodeJS bevat genoeg bestaande oplossingen voor webontwikkeling. Ook niet onbelangrijk: NodeJS i.c.m. Google's v8 engine is gewoon erg snel.

Goed, NodeJS is dus de een aantrekkelijke optie. Hoe nu verder? Welke van de 100en frameworks ga ik gebruiken?

Voor NodeJS zijn 100en, als niet 1000en, frameworks om je ding te doen op het web. Allemaal uniek in features en allemaal met een verse kijk op webdevelopment. Ik heb echter geen klik kunnen vinden met welk framework dan ook.

Ik begon aan mezelf te twijfelen. Ben ik nou zo kieskeurig? Ben ik te moeilijk? Heb ik teveel eisen? "Uiteindelijk moet ik gewoon wat gedaan krijgen, ik kan niet alles zelf gaan maken" dacht ik, hoe graag ik het ook zou willen.

Ik dacht: "Weet je wat? Ik begin gewoon met mijn applicatie en zie later wel welk (web)framework ik ga gebruiken!"

Ik pakte een NodeJS test framework erbij en ging m.b.v. TDD aan de slag met mijn applicatie volgens de patterns omschreven door deze kerel:

https://www.youtube.com/watch?v=WpkDN78P884

Ik begon met het aanmaken van entities (domme datastructuren; vergelijkbaar met modellen), interactions (single-responsibility controllers) en een test repository (een ding voor het opslaan en ophalen van entities)

NodeJS performance wordt bepaald door alle I/O en zware taken asynchroon uit te voeren. Omdat een repository data "persistent" zou moeten opslaan, zou-ie daarvoor I/O operaties moeten uitvoeren. De daadwerkelijke implementatie van de repository interesseert me niet: MySQL, MongoDB, plain text files, CSV files, Excel files of zelfs in-memory: het zal me een worst wezen. Ik weet wel dat de meeste manieren I/O nodig hebben.

Ongeacht de implementatie wil ik het aanroepen van een repository consistent houden; Iedere implementatie moet een identieke interface hebben. Ik zorg er dus voor dat ik mijn repository asynchroon laat zijn "by design" zodat de implementatie ruimte heeft om zijn handelingen asynchroon uit te voeren.

Alle repository methodes zijn dus nu asynchroon, zelfs als dat niet nodig is. Mijn interactions maken gebruik van een repository om eventueel data in op te slaan. Omdat repositories asynchroon werken, moeten mijn interactions ook asynchroon werken voor het geval dat er calls naar een repository nodig zijn. Ook de interface van iedere interaction wil ik consistent houden, dus ook deze maak ik asynchroon by design.

Na een paar daagjes code kloppen had ik een mini applicatie staande. Deze zag er ruwweg zo uit:


code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
entities/
  Project(id: int, name: string, createdAt: datetime, updatedAt: datetime)

interactons/
  CreateProject(name: string)
    => { id: int, name: string, createdAt: datetime, updatedAt: datetime }
  IndexProjects()
    => [{ id: int, name: string, createdAt: datetime, updatedAt: datetime }]

repositories/
  RuntimeRepository
    .putProject()
    .findProjects()
  MysqlRepository
    .putProject()
    .findProjects()



Dat is een mooi begin. Ik heb nu een ultra kleine applicatie waar ik verder mee kan. Met zo'n 150 tests voor achterliggende abstracties om het toevoegen van nieuwe entities, interactions en repositories makkelijker te maken, ben ik klaar om een delivery aan mijn applicatie toe te voegen.

De WebDelivery

Ik wil mijn applicatie eerst op het web hebben. Ik begin dus met het aanmaken van een WebDelivery. Ik stel mezelf de regels dat een delivery nooit toegang krijgt tot een repository of een entity, ook niet als return value. Een delivery mag alleen gebruik maken van de interactions van mijn applicatie.

Voor mijn applicatie ga ik 3 pagina's nodig hebben:
GET /
GET /nieuw-project
POST /nieuw-project

Mijn WebDelivery zal gebruik maken van Express voor routing. Verder zal ik gebruik maken van controllers om de input van een HTTP request om te zetten tot 1 of meerdere calls naar interactions, indien nodig. De controllers koppel ik aan routes in de web delivery.

In tegenstelling tot gebruikelijke controllers in web applicatie frameworks, zullen de controllers waar ik over spreek verantwoordelijk zijn voor een enkele taak. Zo zal ik een HomeController toevoegen voor de home pagina, die voor nu een index van mijn projecten zal genereren, en een CreateProjectController voor het maken van een nieuw project, zowel het renderen van het formulier als het daadwerkelijk aanmaken van het project.

Interactions zijn asynchroon by design omdat deze eventueel async calls naar een repository moet maken. Omdat controllers mogelijk gebruik maken van 1 of meer interactions, zullen de controllers ook weer asynchroon opgezet worden.

De controllers voeren alle interactions uit die nodig zijn en maken o.b.v. hiervan een compleet response object, dat bestaat uit enkel een hash. Deze hash wordt meegegeven aan een template renderer voor het uiteindelijke HTML document in de response.

Alles asynchroon

Deze structuur brengt vele voordelen met zich mee. Ten eerste ben ik volledig onafhankelijk van hoe mijn applicatie geserveerd wordt aan de gebruiker. Dit kan via het web zijn, zoals dit voorbeeld, maar het kan net zo goed via CLI of een desktop app. Hiervoor moet je alleen maar een nieuwe delivery toevoegen.

Als ik nu mijn template renderer dusdanig aanpas dat ik het in fragmenten render, kan ik alvast beginnen met het renderen van een HTML fragment wanneer de achterliggende eventuele interaction klaar is. Het renderen van de HTML maak ik dan asynchroon door het in een worker te doen, net zoals alle andere zwaardere Javascript berekeningen.

Stel dat ik een homepage heb met een index van projecten, een index van todos en een index van ontvangen berichten. Deze pagina zal bestaan uit 3 interactions: IndexProjects, IndexTodos en IndexMessages. Deze 3 interactions kunnen asynchroon tegelijk uitgevoerd worden. Op de pagina zelf zal ik alles tonen in aparte tabellen, dus ik kan direct al een HTML fragment bestaande uit een tabel renderen wanneer de betreffende interaction klaar is, nog voordat de andere interactions klaar zijn met hun rekenwerk.

Zo kan ik een complexe pagina bestaande uit vele onderdelen net zo snel maken als het traagste "blok" in de pagina en worden alle cores van het systeem (of zelfs meerdere systemen) optimaal benut wanneer een zo snel mogelijke response van belang is.

In praktijk gaat dit zeker uitmaken. Als ik kijk naar mijn bestaande websites bestaat vrijwel iedere pagina uit meerdere van dit soort "blokken". Menu's zijn mogelijk dynamisch, een blogpost pagina heeft een blogpost, een blokje met vorige berichten, een blokje met reacties en vast nog wel veel meer.

Ik heb websites gemaakt met pagina's waarin 20 tot 30 van dit soort "blokken" voorkomen. Performance is altijd een issue op zo'n pagina. Vaak is het een homepage of dashboard. Uit ervaring weet ik ook dat de grootste performance issue bij mijn applicaties vaak ligt in het verwerken van de data tot HTML en niet in het ophalen ervan. Er is echter altijd wel rekenkracht beschikbaar op een server om dit asynchroon uit te voeren over meerdere cores of zelfs meerdere servers, wat nu mogelijk is met de asynchrone en modulaire opzet van mijn applicatie.

Conclusie

De conclusies zover is dat alles asynchroon maken van mijn applicatie en alles modulair maken ervoor heeft gezorgd dat mijn applicatie flexibel is bij veranderingen. Verder kan ik al mijn tests draaien in een fractie van een seconde omdat ik zeer zelden meer test dan nodig: Ik hoef geen database te gebruiken bij mijn tests of HTTP calls te doen voor het testen van core features.

Ik weet nog niet wat ik verder met al mijn ontwikkelingen ga doen. Ik ga zeer spoedig fulltime als freelancer aan de slag, dus ik heb de mogelijkheid zelf te bepalen hoe ik mijn werk doe. Ik wil er wat mee doen, maar voor nu kost het allemaal veel tijd om te experimenteren en ontwikkelen voordat ik wat nuttigs kan opzetten.

Voorlopig blijf ik dus werken met Ruby on Rails. Daarnaast zal ik dit project verder ontwikkelen om hopelijk ooit te kunnen showen. Ik heb veel zelf geschreven en vrijwel alles wat ik heb geschreven is modulair, gedekt met tests en herbruikbaar. Misschien kan ik er in de toekomst wat "echte" projecten op ontwikkelen.

Even wat vrolijkheid

Door Gamebuster op vrijdag 21 maart 2014 17:05 - Reacties (8)
Categorie: funvids, Views: 2.531

Ik werd zo somber van al die blogposts... Even wat flauwe zinloze humor ter compensatie, schaamteloos geript van diverse websites:

http://i.imgur.com/1Rj4ABb.jpg

http://i.imgur.com/8JltaxX.gif

http://i.imgur.com/264fVS1.gif

http://i.imgur.com/9MFKZVt.jpg

http://i.imgur.com/Q9N8pKr.jpg

http://i.imgur.com/m93JDCi.gif
http://i.imgur.com/wCbZaBD.gif















Papers, Please

Door Gamebuster op vrijdag 14 maart 2014 10:17 - Reacties (8)
Categorie: funvids, Views: 3.843

Wazige game, maar awesome gemaakt

Kid de Blits - Brief

Door Gamebuster op donderdag 13 maart 2014 14:33 - Reacties (15)
Categorie: quite useless, Views: 2.146

De onverwachte nieuwe collega

Door Gamebuster op dinsdag 11 maart 2014 10:26 - Reacties (35)
Categorie: quite useless, Views: 6.250

Ik kom half slaperig op m'n werk en zie een collega van mij zitten met een ander persoon waarvan ik niet zeker wist of ik hem al kende of niet. Niet heel interessant; dat gebeurt vaker. We hebben hier zoveel over de vloer lopen dat ik daar niet eens meer naar omkijk.

Deze keer was de situatie echter anders, dat bleek al gauw. Toen ik naar mijn stoel liep was er zo'n ongemakkelijk oogcontact moment. Van mijn kant: "Ken ik jou?", van zijn kant wist ik op dat moment nog niet. Dit zette me aan het denken: Ken ik hem? Heb ik 'm nooit eerder gezien? Waarom was er zo'n moment van oogcontact? Is-ie wel nieuw? Is het een nieuwe collega? Nee toch… dan heb ik zo langs 'm gelopen, terwijl hij waarschijnlijk een handje verwachtte, wat het oogcontact verklaarde.

Ik ging zitten. Ik sprak via de chat een collega aan. "Wie is die kerel bij $collega?" vroeg ik hem. "Die vervangt $ex_collega.". "Ah… shit" dacht ik. "Moet je niet even pootje geven? :P" Volgde hij, waarschijnlijk op de hoogte van mijn situatie. Inmiddels zat ik al met koptelefoon op achter de computer met een kop warme thee tussen mij en mijn toetsenbord.

"Ja inderdaad… maar wanneer?" antwoordde ik via de chat met de implicatie dat ik al zat met koptelefoon op en ik 'm eerder negeerde. Hij suggereerde dat ik voor of na het halen van een kop thee of koffie een handje kon schudden zonder dat het al teveel opvalt. Ik had echter al een kop thee, dus dat plan moest even uitgesteld worden.

Vervolgens kwam mijn potentiŽle redding: Nog een collega! Een andere collega loopt nu net binnen. Op het moment dat hij handje schudt kan ik ook opstaan en daarna handje schudden. Ik was erg enthousiast over mijn plan en was overtuigd van een succesvolle afloop. Subtiel volgde ik de bewegingen van mijn zojuist binnengekomen collega, wachtende tot-ie een "pootje gaf"… maar nee. "Goedemorgen!" wenste hij ons toe, waarna ook hij langs de nieuwe collega liep en ging zitten.

Toen mijn collega ging zitten stortte mijn plan ineen. Wat nu? Wacht ik tot ik mijn thee op heb en nieuwe haal? Of spreek ik 'm gewoon aan? Op dat moment staat mijn collega weer op. Hij loopt naar de nieuwe collega en stelt zich netjes voor. Nog voordat ik het doorhad was het alweer voorbij. Ik dacht: "Shit, dat was mijn kans. Sta ik nu op? Wacht ik tot mijn thee?" Op dat moment draait de collega zich om en stelt zich meteen voor aan mij.

Terwijl ik nog in gedachten zat om de situatie in me op te nemen, schudde ik zijn hand en las ik mijn naam hardop aan hem voor. Ook hij vertelde zijn naam. Dit heb ik echter niet onthouden door de onverwachte situatie.

Na het voorstellen omschreef hij dat hij het broertje van mijn baas is: Dan weet ik in ieder geval zijn achternaam. Ik vroeg nog als grap na via de chat "Wat was zijn naam nu?". "Geen flauw idee" reageerde mijn collega.