Cookies op Tweakers

Tweakers maakt gebruik van cookies, onder andere om de website te analyseren, het gebruiksgemak te vergroten en advertenties te tonen. Door gebruik te maken van deze website, of door op 'Ga verder' te klikken, geef je toestemming voor het gebruik van cookies. Wil je meer informatie over cookies en hoe ze worden gebruikt, bekijk dan ons cookiebeleid.

Meer informatie

Door , , 43 reacties

Iteratie #94 is vrijgegeven. In deze sprint hebben we ons gericht op de release van Symfony 3 en de herindeling van General Chat.

Symfony 3

Eind 2015 kwam Symfony's derde versie uit. We zijn bijna gelijk daarna enthousiast in onze composer.json gedoken om 'm aan te passen voor Symfony 3. Helaas bleek dat enthousiasme wat voorbarig, want er was niet alleen sprake van een paar kleine backward incompatible changes; de manier van werken met formulieren was compleet omgegooid. Hoewel de voorbeelden in de Upgrade-guide nog meevallen, hadden sommige van die wijzigingen een paar flinke consequenties.

De wijziging met de meeste gevolgen was: Passing custom data to forms now needs to be done through the options resolver. Uiteindelijk moesten we door alle aanpassingen samen zo'n tachtig formulieren met de bijbehorende aanroepcode controleren en vaak ook aanpassen.

Het had nog wel een klein voordeel; we konden gelijk die specifieke formulieren of de code waarmee ze worden aangeroepen, wat verbeteren. Soms leverde dat flinke vereenvoudigingen op, doordat een oud formulier onhandig was opgezet of onnodig werk 'zelf' deed. Als voorbeeld geldt onderstaand adresformulier. De oude code deed allerlei mapping tussen object en formulier zelf. In werkelijkheid was dat niet nodig, zelfs niet als je, zoals wij, geen Doctrine gebruikt. De oude aanroepcode zag er ongeveer zo uit:

public function addressesOldAction(Request $request, $id = null)
{
$shop = null; // Get the current shop
$address = null; // Get the address

$form = $this->createForm(new ShopPageAddressForm($address));

if ($request->isMethod('post')) {
$form->handleRequest($request);
$data = $form->getData();

if ($form->isValid()) {
if ( ! $id) {
$address = new Address();
}

$address->setShop($shop);
$address->setAddress($data['address']);
$address->setPostalCode($data['zipcode']);
$address->setPlace($data['city']);
$address->setPhoneNumber($data['phone']);
$address->setEmailAddress($data['email']);
$address->setCountry($data['country']);
$address->setAddressTypes($data['addressTypes']);

// Store the address
}
}
}

Door gebruik te maken van de 'dataclass'-functionaliteit van Symfony konden we het reduceren tot het onderstaande, waarbij bovendien het ShopAddressForm zelf wat eenvoudiger werd.

public function addressesAction(Request $request, Shop $shop, Address $address)

{
$form = $this->createForm(ShopPageAddressForm::class, $address);

$form->handleRequest($request);

if ($form->isValid()) {
$address = $form->getData();

// Make sure it has the correct shop, its not in the form.
$address->setShop($shop);

// Store the address
}
}

De Symfony-kenner zal herkennen dat het niet enkel een refactoring van de form logica is. Het stukje code voor Get the address is niet per se nodig en kon hier met een ParamConverter worden opgelost. Dit hebben we ook meteen veel vaker toegepast.

Al met al moesten we veel code aanpassen om Symfony 3 werkend te krijgen, complete statistieken hebben we helaas niet voor handen. Het samenvoegen van de Symfony 3-branch met onze standaardbranch gaf alleen al 455 aangepaste bestanden met 1120 toevoegingen en 1247 verwijderingen.

Herindeling General Chat

Om beter aan te kunnen sluiten op de behoefte van gebruikers van het forum hebben we General Chat in deze iteratie opnieuw ingericht. We hebben de volgende wijzigingen doorgevoerd:

  • 'Werk & Inkomen' hernoemd naar 'Persoonlijke Financiën, Studie en Loopbaan';
  • een nieuw subforum 'Wonen & Verbouwen' aangemaakt;
  • een nieuw subforum 'Sport' aangemaakt;
  • 'Actualiteit, Sport & Politiek' hernoemd naar 'Actualiteit, Wetenschap & Maatschappij';
  • 'Wetenschap & Levensbeschouwing' en 'Nieuws, Sport, TV & Muziek' verwijderd;
  • een duidelijkere splitsing tussen 'besloten' en 'openbaar' aangebracht, zodat de forumindex overzichtelijker wordt.

General Chat nieuwe indeling

En verder…

  • kun je nu filteren op prijs in de listing van grootste prijsdalers in de Pricewatch;
  • hebben we het lazy loaden van images in singlepage-reviews en forumtopics verder verbeterd door de scrollsnelheid te detecteren. Als die onder de 500 pixels per seconde uitkomt, worden ook de afbeeldingen van de huidige zichtbare pagina en de 'volgende' pagina al opgehaald. Zo heb je als gebruiker minder last van afbeeldingen die zichtbaar geladen worden, terwijl de scrollprestaties goed blijven.

Tweakers zoekt een webdeveloper

In het developmentteam van Tweakers is plek vrijgekomen voor een webdeveloper. Ben jij of ken jij iemand die staat te popelen om mee te bouwen aan de ontwikkeling van Tweakers? Bekijk de vacature voor meer informatie.

Moderatie-faq Wijzig weergave

Reacties (43)

De devvers hebben zich dus deze sprint voornamelijk bezig gehouden met het mergen en oplossen van de laatste Symfony 3 issues? Was er nog een hoop werk aan te doen? In de tekst word alleen over 80 forms gesproken die aangepast/reviewed moesten worden, maar jullie zijn er ook al een tijdje mee bezig (note: ik wil niet insinueren dat jullie traag werken oid :P). Zat er soms heel veel vooronderzoek in en hebben jullie deze sprint gebruikt om echt alles om te gooien?

Zou het misschien eens een idee zijn om een video/review te maken van hoe jullie werken met je scrum process? Zeg maar een verhaaltje van change request tot oplevering. Ook hoe jullie het hele deployment proces ingeregeld hebben.

Voor de tweakers die in de dev wereld zitten zal het weinig nieuws zijn (of misschien juist betere/andere inzichtinen voor hun eigen workflow). Maar voor tweakers die alleen weten dat je scrum iets is van rugby en wat met programmeren te maken heeft is het misschien interessant. (+ we zien welke devvers nu Tweakers maken!)

[Reactie gewijzigd door ThaStealth op 15 november 2016 14:03]

Ik vind het wel een leuk idee. En van die feedback kunnen ook wij weer leren. Dat kost natuurlijk wel enige tijd maar ik zal het eens in de groep gooien :)
Sterker nog, productteam en ik hebben daar al wat mails over gewisseld. We denken nog na over een goede manier. Is dit bijvoorbeeld leuk? Voorbeelden en suggesties zijn van harte welkom.
Hoe het in dat filmpje gedaan word is een goede kapstok om het hele scrum 'verhaal' aan op te hangen, maar verder ook nog uitweiden over het versiebeheersysteem, build server opzet, deployment, issue tracking, etc, tevens ook beelden van de verschillende meetings erbij.

Zo is een 15 min scrum meeting waar de 3 vragen door ieder lid beantwoord worden logisch en niet echt spannend, maar bijv ook laten zien hoe het bij Tweakers gaat (ook triviale dingen zoals staat iedereen echt om de koffieautomaat heen of is er een aparte plek, hoe word de volgorde van sprekers bepaald, etc). Niet al deze zaken zijn echt 100% informatief, maar het geeft ook een leuke inkijk in hoe het werken echt bij Tweakers is.

Ook simpele dingen zoals een scrum board en hoe het zichtbaar gemaakt word in het pand (is het een fysiek bord wat ergens met magneten hangt en 2x per dag bijgewerkt word of hangen door het hele pand UHD schermen waarop ieder moment van de dag de status te zien is ik ga ervanuit dat het optie 2 is. Dit soort dingen geven net wat meer info.

[Reactie gewijzigd door ThaStealth op 15 november 2016 17:12]

Zo'n animatie geeft wrs meer inzicht dan een video met een kijkje in de keuken (hoewel dat ook wel leuk kan zijn :)).
Wat mij wel interessant lijkt, is hoe het team het testproces heeft geimplementeerd over de iteraties. Welke aspecten (regressie maar ook b.v. usablity of security of load testing) worden op welke manier getest tijdens de sprints. En hoe beweegt de risicoanalyse mee over de iteraties.
Naast forms is ook de complete directory structure aangepast inclusief test directory structure en namespaces en daarbij natuurlijk nog 3rd party bundles die compatible moeten blijven. Ik denk at je je snel verkijkt op de hoeveelheid werk.
En het testen daarvan
Testen kost bij ons idd meer tijd dan het creëren.
"Het samenvoegen van de Symfony 3-branch met onze standaardbranch gaf alleen al 455 aangepaste bestanden met 1120 toevoegingen en 1247 verwijderingen"

Dit zouden wij nooit doen, big bang releases zijn dus danig gevaarlijk dat je een hoop fouten riskeert. Waarom niet per formulier een commit en "meteen" live zetten zodra het getest is? Zolang je 2.8 draait kan je het gewoon forward compatible maken.
Dat hebben we voor een groot deel ook gedaan. Er is de afgelopen tijd al veel aangepaste code gereleased. Vooral het deel dat niet backwards compatible was is met deze release meegenomen :)
Afgelopen abofest is over scrum (binnen Tweakers) een sessie geweest :)

[Reactie gewijzigd door P1nGu1n op 15 november 2016 13:55]

De devvers hebben zich dus deze sprint voornamelijk bezig gehouden met het mergen en oplossen van de laatste Symfony 3 issues?
In de .plan melden we normaal gesproken alleen wijzigingen die direct voor de gebruiker beschikbaar zijn. Werk dat nog niet gereleased kan worden melden we niet. In de afgelopen iteratie hebben de devvers de meeste tijd besteed aan projecten die we nu nog niet kunnen presenteren :) .
Het lijkt er op dat er nog steeds entities aan formulieren gebind worden, dat raad ik af. Simpelweg omdat het forceerd om je entities in een invalide staat te houden:
- https://stovepipe.systems/post/avoiding-entities-in-forms
- https://ocramius.github.io/doctrine-best-practices/#/47

[Reactie gewijzigd door Zayl op 15 november 2016 14:25]

Als je objecten opslaat zonder te valideren dan heb je wel een groter probleem in je code ;) Validatie doen in setters zoals in de eerste post vind ik ook niet correct overigens. Persoonlijk ben ik meer pragmatist dan purist, zeker voor non-mission-critical code :)
Je object moet altijd in een valide staat zijn, als dat betekend dat een bepaald veld mutable is (immutable is beter), dan moet de setter wel degelijk valideren of de nieuwe waarde toegestaan is. In het geval van data objecten voor formulier maakt dit geen bal uit, maar voor entities is dit wel degelijk een must have.
Een validerende setter betekent dus dat je ook elke keer weer validatie gaat doen van data waar je eigenlijk al van weet dat 'ie gewoon klopt (teruglezen uit de database bijvoorbeeld). Verder is het niet backwards-compatible bij verandering van je validatie-rules waarbij je een uitzondering maakt voor bijvoorbeeld reeds bestaande situaties.

Je bent heel stellig met het woordje 'moet', maar imo is dat ook gewoon een mening gebaseerd op een puristische (en in mijn ogen soms onnodig complicerende) visie ;)
Als je met een grote database werkt en tientallen ontwikkelaars en grote websites dan is het inderdaad moet. Het kan zomaar komen dat er door een bug toch een verkeerde waarde in valt (dan heb ik het niet over een regex validatie etc), denk aan een te lange string die afgekapt wordt. Het kan ook zijn dat een lege waarde wordt ingevuld in een nullable field, dit is dan een empty string.

Ik heb te veel data corruptie gezien omdat dit niet goed of volledig gevalideerd wordt.

edit:
Een validerende setter betekent dus dat je ook elke keer weer validatie gaat doen van data waar je eigenlijk al van weet dat 'ie gewoon klopt (teruglezen uit de database bijvoorbeeld).

Dit is dus niet de bedoeling. Je setter moet niet worden aan geroepen als je een object hydrate, wat nou als ik bv geen setter heb ;)

[Reactie gewijzigd door Zayl op 16 november 2016 14:19]

Er is een verschil tussen ongeldige invoer (iets wat type safty en Value objects al hadden moeten afvangen) en data die niet voldoet aan bepaalde eisen (zoals maximale lengte of hoeveelheid op basis van bepaalde criteria, ook wel invariants genoemd). Een Entity mag nooit data bevatten die ongeldig is! Dus geen DateTime object in een integer veld.

Het is niet ongebruikelijk dat de "staat" van je Entity tijdelijk ongeldig is (je kan immers niet meerdere velden valideren op basis van één waarde). Dat kan later worden gevalideerd, al dan niet met behulp van een service (die je aan de Entity meegeeft via de Constructor/Reconstructor of validate(ValidatorService $validator) method).

Wat achter vaak verkeerd gedaan word is dat er een directe koppeling is tussen de Entity en de Form (of UI laag), waarmee het risico bestaat dat je Entity ongeldige invoer "moet" accepteren omdat je anders een foutmelding krijgt (die je niet mooi kan afvangen).

Daarnaast is het moeilijk om je Entity verder te ontwikkelen zonder ook de Form en al het andere dat daarvan afhankelijk is aan te moeten passen (zelfs als zij niets met die wijzing te maken hebben), DTO's bieden echter de mogelijkheid om je Data/Domain laag gescheiden te houden van deze UI details zodat ipv van beide te dwingen om samen te werken, je een adapter kan maken die data tussen twee lagen (of systemen) vertaald.

Wel moet je er voor waken dat je DTO niet alle validatie regelt (dat je alleen de DTO staat valideert), een DTO is niet anders dan een communicatie middel, de Entity blijft altijd verantwoordelijk voor zijn eigen data.

Dit is met name raadzaam bij grote applicaties waar data consistentie van groot belang is, voor een simpele CRUD applicatie (Create, Read, Update, Delete) is dit echter meer werk dan het oplevert. Zolang je geen ORM gebruikt of zonder enig overzicht alles flushed is er bijna geen risico.

Over de setter discussie, setters zijn niet per definitie slecht of zo maar worden vaak gebruikt om een Entity te dwingen iets zijn (setActive, setDisabled) dan dat je op met de Entity communiceert (activate, disable) en dus intentie duidelijk maakt (Een object is niets anders dan een abstractie van een concept, als je een lichtschakelaar interface zou maken gebruik je toch ook niet setOn/setOff()? ).
Dit mag dus niet gebeuren; "Het is niet ongebruikelijk dat de "staat" van je Entity tijdelijk ongeldig is": https://ocramius.github.io/doctrine-best-practices/#/50

Als er namelijk een exception wordt gegooit en je hebt bv een mooie error handler die een flush() aan roept, ben je de pineut en wordt het naar je database weggeschreven. (om maar een voorbeeld te noemen).
Ik denk dat jij het dan echt specifiek over doctrine hebt? Dat gebruiken wij namelijk niet. Hoe je omgaat met 'dirty data' is dan ook deels afhankelijk van je architectuur, en daar kan je (indien noodzakelijk) ook op andere manieren voor zorgen dat het niet 'per ongeluk' kan worden weggeschreven.
The blog post gaat specifiek over doctrine omdat het een veel voorkomend probleem is. Echter kan je dit ook toepassen op bv propel of eloquent (al ben ik geen fan van AR). Doctrine hydration gaat rechtstreeks de properties in.
Ik bedoel informatie staat ;) dus (een overschreven hoeveelheid van een product dat om veiligheidsredenen niet mag worden overschreven bijvoorbeeld).

http://verraes.net/2015/02/form-command-model-validation/
http://verraes.net/2013/0...ony2-forms-from-entities/

Dat kan je alleen pas achteraf valideren, door aan de Model te vragen om zijn eigen staat te valideren via een validator.

[code]
class Location
{
// ...
public function validate(LocationValidationHandler $validationHandler)
{
$validator = new LocationValidator($this, $validationHandler);
$validator->validate();
}
}
[/code]

Bron: https://leanpub.com/ddd-in-php

Uiteraard gaat dit veel verder dan simpel flush aanroepen in de Controller, je maakt een afgeschermd gedeelte waarin je dit alles regelt. Doctrine (ORM) is dan slechts een implementatie detail. Ik zou zelfs dan zover gaan om iedere bounded context (Gebruikerssysteem, nieuws, shop) een Eigen EntityManager te geven en alleen via Messages/Adapters informatie communiceren (ook wel Hexagonal architecture genoemd).

De link die jij geeft gaat puur over Doctrine, als je zonder enige logische flow zomaar flush aanroept dan kan er donder op zeggen dat het een keer fout gaat.
Zelf als je geen volledige DDD gebruikt of iets is het nog steeds raadzaam om een deel daarvan toe te passen op je ontwerp zodat je minder afhankelijk bent van Doctrine als detail.
Erg leuk om te lezen hoe snel jullie eigenlijk deze iteratie met Symfony 3 zijn opgeschoten. Staat het inmiddels ook al live?
Uurtje geleden hebben ze kleine downtime gehad en ik geloof dat het toen omgewisseld is.
Deze iteratie was alleen het terugmergen en testen voor zover ik weet, er werd weinig code aan geklopt (tenzij de testers iets vonden uiteraard). Het staat dus nu ook live zodat het nog beter getest kan worden ;)

Aan de code zelf is een goede 3 maand gewerkt (we hebben natuurlijk gewoon een roadmap te volgen, dus zodra het uitkwam en we merkten dat het een groot project zou worden moesten we hem inplannen)

[Reactie gewijzigd door Kees op 15 november 2016 14:09]

Voor mijn gevoel staat het al live
Zeer netjes gedaan guys!

Doe zelf ook flink wat met Symfony 3 en zou perfect in het profiel passen wat jullie zoeken, enige nadeel is, ik ga op dit moment naar school om dat papiertje te halen :(

Ow, nog een vraagje, werken jullie recht uit de database of gebruiken jullie Redis als soort tussen-laagje?

Merk trouwens wel dat WYSIWYG gebroken is qua commenting, en dat laadtijden nogal traag zijn...?

[Reactie gewijzigd door Anthraxium-64 op 15 november 2016 13:54]

We gebruiken mysql direct, met memcache als tussenlaag, met mongodb als tussenlaag en met activemq als tussenlaag. Er is geen one-size fits all dus voor diverse taken gebruiken wij diverse programma's.

Redis zit daar overigens niet tussen.
Je vergeet de Engine nog als tussenlaag :P
'Nieuws, Sport, TV & Muziek' verwijderd
Volgens mij is dat ook gewoon een nieuw forum geworden genaamd "Film, Tv en Muziek"?
Dat forum bestond al een tijd. NSTM heeft lang als read-only archief geleefd voor oude topics. Nu is NSTM weg.
Doen jullie ook wat met HHVM of PHP 7?
HHVM is niet compatible met alles van PHP 5.6+ , dat is best een nadeel. Daarnaast moet je je deployment anders aanpakken. Ik zeg niet dat dit alles slecht is*, maar helemaal nu php 7 zo enorm veel sneller is t.o.v. de 5.x versies moet je overstappen wel goed kunnen onderbouwen.

* Bij FB is het blijkbaar nog steeds een prima plan, en genoeg andere partijen.
Ik hoor anders wel veel positieve verhalen over PHP 7 en HHVM, zijn beide compatible met elkaar?
Zou het wel eens leuk vinden om te weten hoe jullie werken met versiebeheer. :)
Er staat wel iets over branches, maar dat is niet het gehele plaatje.
Uitgebreide details hoeven niet, maar een globaal idee is altijd interessant.

Anyway, keep up the good work! Zeker naar lazyloading ben ik erg benieuwd of deze nu beter werkt.
Mijn persoonlijke ervaring is dat Git het beste werkt, Subversion heb ik vroeger meegewerkt (een ramp vergeleken met Git, hoe vaak een simpele merge niet verkeerd liep :( ) en Mercurial is eigenlijk Git light (maar dan minder gebruiksvriendelijk).

Git werkt echt super, als je eenmaal de basis doorhebt wil je niet anders meer (tenzij het vergelijkbaar werkt :9 )

Er zijn in wezen twee polaire Git workflows: =http://nvie.com/posts/a-...w (door Vincent Driessen) en de GitHub flow (waarmee je altijd gebruik maakt van feature branches en die na voltooiing merged naar de master branch, zo ontwikkeld ook GitHub zelf hun software). http://scottchacon.com/2011/08/31/github-flow.html

* s.stok Toevallig heb ik net vandaag mijn eigen tool voor GitHub management online gezet (BETA kwaliteit, er zitten nog wat kinderziektes in :X ). https://github.com/park-manager/hubkit
Uiteindelijk moesten we door alle aanpassingen samen zo'n tachtig formulieren met de bijbehorende aanroepcode controleren en vaak ook aanpassen.
Bij mij speelt meer de vraag of er geautomatiseerde (regressie)testen aanwezig zijn? Klinkt namelijk alsof bovenstaande quote met de hand uitgevoerd is, wat als een behoorlijk effort klinkt.

[Reactie gewijzigd door Barbas op 15 november 2016 15:01]

hebben we het lazy loaden van images in singlepage-reviews en forumtopics verder verbeterd door de scrollsnelheid te detecteren. Als die onder de 500 pixels per seconde uitkomt, worden ook de afbeeldingen van de huidige zichtbare pagina en de 'volgende' pagina al opgehaald. Zo heb je als gebruiker minder last van afbeeldingen die zichtbaar geladen worden, terwijl de scrollprestaties goed blijven.
Hartelijk dank Devvers. Wat vervelend was dat! Thanks *O*
Woops, caching gaat beetje fout?

[Reactie gewijzigd door Anthraxium-64 op 15 november 2016 13:55]


Om te kunnen reageren moet je ingelogd zijn



Nintendo Switch Google Pixel Sony PlayStation VR Samsung Galaxy S8 Apple iPhone 7 Dishonored 2 Google Android 7.x Watch_Dogs 2

© 1998 - 2016 de Persgroep Online Services B.V. Tweakers vormt samen met o.a. Autotrack en Carsom.nl de Persgroep Online Services B.V. Hosting door True