Wat is een het beredeneren van bouwkeuzes?
Het waarborgen van kwaliteit betekent dat je niet alleen kijkt naar de directe werking van de code, maar ook naar aspecten als leesbaarheid, onderhoudbaarheid, uitbreidbaarheid en testbaarheid. Het onderbouwen van deze keuzes helpt niet alleen om de motivatie achter bepaalde keuzes te begrijpen, maar zorgt er ook voor dat andere teamleden (en eventueel toekomstige ontwikkelaars) de logica achter beslissingen kunnen volgen.
Hoe zit het beredeneren van bouwkeuzes in elkaar?
Bij het maken van een beredeneerde bouwkeuze is het van belang om eerst inzicht te krijgen in de vereisten en context van het project. Dit houdt in dat de ontwikkelaar begrijpt welke functionaliteiten vereist zijn en welke technische beperkingen, zoals performance, schaalbaarheid en beveiliging, van toepassing zijn. Daarnaast moet de bestaande architectuur en de mogelijkheid tot toekomstige uitbreidingen worden meegenomen in de afweging. Ook kan gedacht worden aan code richtlijnen met betrekking tot testen, commentaar en formatting.
Vervolgens moeten verschillende alternatieven worden overwogen. De ontwikkelaar onderzoekt meerdere oplossingen en vergelijkt ze op basis van de vereisten, waarbij factoren zoals prestaties, onderhoudbaarheid, kosten en compatibiliteit met bestaande systemen een rol spelen. Door alternatieven te evalueren, kan de beste keuze worden gemaakt die het meeste voordeel biedt binnen de projectdoelen.
Bij de keuze voor een taalconcept moet het concept worden afgestemd op de eisen van het project. De keuze van het taalconcept heeft invloed op de onderhoudbaarheid, uitbreidbaarheid en testbaarheid van de oplossing.
Casus
Stel je voor dat je werkt aan een project waarbij je een applicatie bouwt voor het beheren van klantbestellingen voor een e-commerceplatform. Het platform moet zowel kleine als grote bestellingen kunnen verwerken, de bestellingen opslaan, en een status bijhouden van elk product in het bestelproces. De applicatie moet daarnaast ook goed schalen om te voldoen aan pieken in de belasting, zoals tijdens promoties of feestdagen.
Stap 1: Vereisten en context
De applicatie heeft enkele belangrijke vereisten:
- Schaalbaarheid: De applicatie moet in staat zijn om een groot aantal bestellingen per dag te verwerken.
- Modulariteit: Het moet eenvoudig zijn om de functionaliteit uit te breiden (bijvoorbeeld met nieuwe betaalmethoden of verzendopties).
- Testbaarheid: De applicatie moet goed getest kunnen worden, met unit tests voor elke afzonderlijke functionaliteit.
- Beheerbaarheid: De applicatie moet eenvoudig te onderhouden zijn, met duidelijke scheiding van verantwoordelijkheden.
Stap 2: Alternatieven
Twee mogelijke oplossingen worden overwogen:
Gebruik van Dependency Injection (DI): Hierbij zou de applicatie alle afhankelijkheden zoals databaseconnecties, API-aanroepen en serviceklassen injecteren via een DI-container. Dit zou het mogelijk maken om de applicatie gemakkelijk te testen, omdat alle externe afhankelijkheden eenvoudig kunnen worden gemockt in tests. DI zou ook de modulariteit verhogen, omdat het makkelijker zou zijn om de implementatie van afhankelijkheden te vervangen zonder de rest van de code te beïnvloeden.
Handmatige afhankelijkheden zonder DI: Bij deze oplossing zou elke klasse zelf instantiëren wat het nodig heeft (bijvoorbeeld een databaseverbinding of een extern API-request). Dit maakt de code eenvoudiger en minder afhankelijk van een DI-framework. Echter, het zou ook de testbaarheid bemoeilijken, omdat het moeilijker zou zijn om de afhankelijkheden te vervangen door mocks in unit tests. Ook zou het onderhoud moeilijker kunnen worden doordat het aanpassen van één component mogelijk andere componenten in de applicatie beïnvloedt.
Stap 3: Keuze voor een oplossing
Na het afwegen van de alternatieven blijkt Dependency Injection (DI) de betere keuze voor deze applicatie. De reden hiervoor is dat de applicatie meerdere services heeft die onderling afhankelijk zijn, zoals de
OrderService
, dePaymentService
, en deShippingService
. Door DI te gebruiken, kunnen deze services worden losgekoppeld van hun afhankelijkheden en wordt het gemakkelijker om ze onafhankelijk te testen en te onderhouden.Bovendien biedt DI voordelen op het gebied van uitbreidbaarheid: als er in de toekomst nieuwe diensten of afhankelijkheden moeten worden toegevoegd (bijvoorbeeld een nieuwe betalingsgateway of een externe verzendservice), kunnen deze gemakkelijk worden geïnjecteerd zonder dat de bestaande code moet worden aangepast. Dit verhoogt de modulariteit van de applicatie en maakt het gemakkelijker om de code in de toekomst uit te breiden.
Conclusie:
Het kiezen voor Dependency Injection (DI) biedt de juiste balans tussen modulariteit, testbaarheid en uitbreidbaarheid. Het maakt het mogelijk om de applicatie gemakkelijk te schalen en te onderhouden door afhankelijkheden dynamisch in te voegen, wat cruciaal is voor een e-commerceplatform dat in de toekomst mogelijk zal uitbreiden met nieuwe functionaliteit. Door DI toe te passen, wordt het eenvoudiger om de codebase schoon, goed gestructureerd en goed testbaar te houden.
Hoe gebruik je het beredeneren van taalconcepten?
Keuzes en redeneringen voor taalconcepten zijn niet alleen van belang tijdens de ontwikkelfase, maar kunnen ook een belangrijk onderdeel zijn van het ontwerp van de applicatie. Het ontwerp van de architectuur en de structuur van de code vereist het selecteren van geschikte taalconcepten die de beheersbaarheid, uitbreidbaarheid en onderhoudbaarheid van het systeem ten goede komen. Het kiezen van bijvoorbeeld objectgeoriënteerde concepten zoals encapsulatie en polymorfisme of functionele concepten zoals immutability kan een aanzienlijke invloed hebben op de prestaties en de leesbaarheid van de code op lange termijn. Dergelijke keuzes vormen dus de ruggengraat van het technische ontwerp en dragen bij aan de stabiliteit en flexibiliteit van de applicatie.
Daarnaast speelt de keuze voor taalconcepten ook een rol in de testfase van de applicatie. Wanneer je bijvoorbeeld kiest voor een functioneel ontwerp, kan het eenvoudiger zijn om tests te schrijven die ongewijzigde gegevens (immutability) en pure functies benutten, wat de betrouwbaarheid van de tests ten goede komt. Aan de andere kant kunnen objectgeoriënteerde benaderingen die gebruikmaken van complexere objectstructuren meer geavanceerde teststrategieën vereisen, zoals mocken of dependency injection. Het begrijpen van de taalconcepten kan helpen bij het ontwerpen van tests die goed aansluiten bij de gekozen architectuur.
Verder kunnen de keuzes in taalconcepten ook een impact hebben op schaalbaarheid en performance, vooral wanneer applicaties in een productieomgeving draaien en te maken krijgen met grotere hoeveelheden data of een hogere mate van parallelisme. Taalconcepten kunnen bijvoorbeeld het geheugenbeheer of de mogelijkheid voor asynchrone uitvoering beïnvloeden, wat essentieel is bij het optimaliseren van de prestaties van de applicatie.
Tot slot kunnen de juiste taalconcepten de ondersteuning voor nieuwe functionaliteiten vergemakkelijken. Door in het ontwerp de juiste keuzes te maken, kan een applicatie makkelijker nieuwe features verwerken zonder dat dit ten koste gaat van de bestaande codebasis, waardoor de levensduur van het systeem wordt verlengd.
Volgende stap: Oplossen fouten