We bouwen infrastructuur die vrijwel nooit mag falen. Om dit te bereiken, bewegen we snel terwijl we geweldige kwaliteitssoftware leveren zonder ergens op te beknibbelen of concessies te doen. Dit document schetst de engineeringstandaarden die ons zullen begeleiden tot 2026 en verder.
Teamstructuur
Onze ingenieursorganisatie bestaat uit vijf kernteams, elk met verschillende verantwoordelijkheden:
Foundation Team: Richt zich op het vaststellen en onderhouden van coderingsnormen en architectuurpatronen. Dit team werkt samen met andere teams om beste praktijken organisatiebreed vast te stellen.
Consumenten-, Ondernemings- en Platformteams: Productgerichte teams die snel functies leveren terwijl ze voldoen aan de kwaliteitsnormen die in dit document zijn vastgelegd. Deze teams bewijzen dat snelheid en kwaliteit niet elkaar uitsluiten.
Community Team: Verantwoordelijk voor het snel beoordelen van PR's van de open-source community, het geven van feedback en het begeleiden van dat werk naar merge. Dit team zorgt ervoor dat onze open-source bijdragers een geweldige ervaring hebben en dat hun bijdragen voldoen aan onze kwaliteitsnormen.
Onze Resultaten Tot Nu Toe
De data spreekt voor zich. In het afgelopen anderhalf jaar hebben we fundamenteel getransformeerd hoe we software bouwen:

We hebben ons ingenieursresultaat ongeveer verdubbeld terwijl we tegelijkertijd de kwaliteit verbeterden. Nog indrukwekkender is de verschuiving in wat we bouwen:

We hebben met succes ongeveer 20% van de inspanning van ingenieurs herverdeeld van fixes naar functies, prestatieverbeteringen, refactors, en klussen. Deze verschuiving toont aan dat investeren in kwaliteit en architectuur je niet vertraagt. Het versnelt je.
Cal.com's Basis Maakt Uitmuntendheid Mogelijk voor coss.com
Cal.com is een stabiel, winstgevend bedrijf dat we zullen blijven laten groeien.
Dit succes geeft ons een uniek voordeel bij het bouwen van coss.com. In tegenstelling tot de vroege dagen van Cal.com, waar we snel moesten handelen om product-markt fit te bereiken en een duurzaam bedrijf op te bouwen, begint coss.com vanuit een sterke positie.
We hoeven ons niet te haasten met coss.com.
De stabiliteit van Cal.com betekent dat we ons kunnen veroorloven om coss.com vanaf dag één op de juiste manier te bouwen. We hebben het voorrecht om deze ingenieursnormen te implementeren zonder de druk van directe markteisen of financieringsbeperkingen. Dit is een fundamenteel andere startpositie.
De 'traagheid' is een investering, geen kosten.
Ja, het volgen van deze normen kan initieel langzamer aanvoelen en kan zelfs frustrerend zijn voor sommige ingenieurs. Het schrijven van DTO's kost meer tijd dan het rechtstreeks naar de frontend doorgeven van database typen. Het creëren van goede abstracties en dependency-injectie vereist meer voorafgaande ontwerp. Het aanhouden van 80%+ testdekking voor nieuwe code vraagt discipline. Maar deze schijnbare traagheid is tijdelijk, en de beloning is exponentieel.
Overweeg de samengestelde rendementen...
Code die vanaf het begin correct is ingericht, heeft later geen massale refactors nodig
Hoge testdekking voorkomt bugs die anders weken van debuggings- en hotfixes zouden vergen (zie 2023 tot midden 2024)
Goede abstracties maken het veel sneller toevoegen van nieuwe functies in de loop der tijd
Schone grenzen en DTO's voorkomen de architecturale erosie die uiteindelijk volledige herschrijvingen vereist
Het Cal.com traject laat zien wat er gebeurt als je optimaliseert voor onmiddellijke snelheid. Hoge aanvankelijke snelheid die geleidelijk afneemt, omdat technische schuld zich opstapelt, architecturale shortcuts knelpunten creëren, en meer tijd wordt besteed aan probleemoplossing dan aan het bouwen van functies (zie eerdere grafiek waar we 55-60% van de inspanning van ingenieurs aan fixes besteedden).
Het coss.com traject zal de kracht omarmen van vanaf dag één goed bouwen. Iets tragere initiële snelheid bij het vaststellen van goede patronen, gevolgd door exponentiële versnelling naarmate die patronen rendementen opleveren en snellere ontwikkeling met meer vertrouwen mogelijk maken.
Kernprincipes
1. Geen uitgestelde kwaliteit
We minimaliseren 'ik doe het in een vervolg-PR' voor kleine refactors.
Vervolg-PR's voor kleine verbeteringen worden zelden gerealiseerd. In plaats daarvan stapelen ze zich op als technische schuld die ons maanden of jaren later belast. Als er nu een kleine refactor gedaan kan worden, doe het dan nu. Vervolgacties moeten worden gereserveerd voor aanzienlijke veranderingen die daadwerkelijk aparte PR's rechtvaardigen of voor uitzonderlijke, dringende gevallen.
2. Hoge normen in code review
Laat PR’s niet door met veel kleinigheden alleen om te vermijden de ‘slecht persoon’ te zijn.
Dit is precies hoe codebases na verloop van tijd slordig worden. Code review gaat niet over aardig zijn. Het gaat over het handhaven van de kwaliteitseisen die onze infrastructuur vereist. Elke kleinigheid doet ertoe. Elke patroonovereenkomst doet ertoe. Pak ze aan voordat je merge, niet erna.
3. Duw elkaar om het juiste te doen
We houden elkaar verantwoordelijk voor kwaliteit
Hoeken afsnijden kan sneller aanvoelen op dat moment, maar het creëert problemen die iedereen later vertragen. Wanneer je ziet dat een teamgenoot op het punt staat een PR met duidelijke problemen te mergen, zeg er iets van. Wanneer iemand een quick hack voorstelt in plaats van de juiste oplossing, duw dan terug. Wanneer je in de verleiding komt om tests over te slaan of architecturale patronen te negeren, verwacht dan dat je teamgenoten je uitdagen.
Dit gaat niet over moeilijk zijn of mensen vertragen
Het gaat over collectief eigenaarschap van onze codebase en onze reputatie. Elke shortcut die een persoon neemt, wordt ieders probleem. Elke hoek die vandaag wordt afgesneden, betekent meer debuggingssessies, meer hotfixes, en meer gefrustreerde klanten morgen.
Maak het normaal om slechte beslissingen respectvol uit te dagen
Als iemand zegt "laten we dit nu even hard coden", moet de verwachte reactie zijn: "wat zou het vereisen om het meteen op de juiste manier te doen?" Als iemand onvergetest code wil committen, moet het team terugduwen. Als iemand voorstelt om te kopiëren en plakken in plaats van een goede abstractie te creëren, noem het dan respectvol.
We bouwen iets dat bijna nooit mag falen
Dat niveau van betrouwbaarheid gebeurt niet per ongeluk. Het gebeurt wanneer elke ingenieur zich verantwoordelijk voelt voor kwaliteit, niet alleen hun eigen code maar het hele systeem. We slagen als een team of falen als een team.
4. Streef naar eenvoud
Prioriteit geven aan duidelijkheid boven slimheid
Het doel is code die snel te lezen en te begrijpen is, niet elegante complexiteit. Simpele systemen verminderen de cognitieve belasting voor elke ingenieur.
Stel jezelf de juiste vragen
Los ik eigenlijk het probleem op dat ik moet oplossen?
Denk ik te veel na over mogelijke toekomstige use cases?
Heb ik ten minste één ander alternatief overwogen om dit op te lossen? Hoe vergelijkt het?
Eenvoudig betekent niet zonder functies
Alleen omdat ons doel is om eenvoudige systemen te maken, betekent dit niet dat ze anemisch moeten aanvoelen en voor de hand liggende functionaliteit moeten missen.
5. Automatiseer alles
Maak gebruik van AI
Genereer 80% van boilerplate en niet-kritieke code met AI, waardoor we ons volledig kunnen concentreren op complexe bedrijfslogica en kritische architecturen.
Creëer alarmen zonder ruis en slimme foutafhandeling.
Handmatig testen is steeds meer verleden tijd. AI kan snel en intelligent grote testsuites voor ons maken.
Onze CI is de eindbaas
Alles in dit standaarddocument wordt gecontroleerd voordat code wordt samengevoegd in PR's
Er komen geen verrassingen in main terecht
Checks zijn snel en nuttig
Architecturale Standaarden
We gaan over naar een strikt architecturaal model gebaseerd op Vertical Slice Architecture en Domain-Driven Design (DDD). De volgende patronen en principes zullen rigoureus worden afgedwongen in PR-reviews en via linting.
Vertical Slice Architecture: packages/features
Onze codebase is georganiseerd op basis van domein, niet door technische laag. De packages/features-directory is het hart van deze architectonische benadering. Elke map binnenin vertegenwoordigt een complete verticale slice van de applicatie, gedreven door het domein dat het raakt.
Structuur:
Elke functiemap is een zelfvoorzienende verticale slice die alles bevat wat nodig is voor dat domein:
Domeinlogica: De kernbedrijfregels en entiteiten die specifiek zijn voor die functie
Applicatiediensten: Use case-orkestratie voor dat domein
Repositories: Gegevens die specifiek zijn voor de behoeften van die functie
DTO's: Gegevensoverdrachtobjecten voor het oversteken van grenzen
UI-componenten: Frontend-componenten gerelateerd aan deze functie (waar van toepassing)
Tests: Unit-, integratie- en e2e-tests voor deze functie
Waarom Vertical Slices Belangrijk Zijn
Traditionele gelaagde architectuur is georganiseerd op technische kwesties:
Dit creëert verschillende problemen:
Wijzigingen in één functie vereisen het aanraken van bestanden verspreid over meerdere directories
Het is moeilijk om te begrijpen wat een functie doet omdat de code gefragmenteerd is
Teams lopen elkaar voor de voeten bij het werken aan verschillende functies
Je kunt niet gemakkelijk een functie extraheren of afschaffen
Verticale slice-architectuur is georganiseerd op basis van domein:
Dit lost deze problemen op:
Alles wat betrekking heeft op beschikbaarheid bevindt zich in
packages/features/availabilityJe kunt de hele beschikbaarheidsfunctie begrijpen door één directory te verkennen
Teams kunnen aan verschillende functies werken zonder conflicten (als het Cal.com ingenieursteam groeit, maar zeker in coss.com zullen we teams grote pakketten laten nemen)
Functies zijn losjes gekoppeld en kunnen onafhankelijk evolueren
Richtlijnen voor Het Organiseren van Functies
In theorie is elke functie onafhankelijk inzetbaar. Hoewel we ze misschien niet echt afzonderlijk inzetten, dwingt deze organisatie ons om de afhankelijkheden duidelijk te houden en de koppeling minimaal te houden. Dit is het uitgangspunt en het succes van microservices, hoewel we nog geen microservices zullen implementeren.
Functies communiceren via goed gedefinieerde interfaces. Als boekingen beschikbaarheidsgegevens nodig heeft, importeert het van @calcom/features/availability via geëxporteerde interfaces, niet door in interne implementatiedetails te duiken.
Gedeelde code leeft op geschikte plaatsen:
Domeinonafhankelijke hulpprogramma's en cross-cutting concerns (authenticatie, loggen) :
packages/libGedeelde UI-primitieven:
packages/ui(en binnenkort coss.com ui)
Domeingrenzen worden automatisch afgedwongen. We zullen linting bouwen die voorkomt dat u de interne van functies binnendringt waar u niet mag komen. Als packages/features/bookings probeert te importeren van packages/features/availability/services/internal, blokkeert de linter het. Alle cross-feature afhankelijkheden moeten via de openbare API van de functie gaan.


Nieuwe functies beginnen als verticale slices. Bij het bouwen van iets nieuws, maak een nieuwe map in packages/features met de complete verticale slice. Dit maakt het duidelijk wat je bouwt en houdt alles vanaf dag één georganiseerd.
Voordelen
Ontdekbaarheid
Zoek je boekingslogica? Het staat allemaal in
packages/features/bookings. Niet nodig om te zoeken door controllers, services, repositories en hulpprogramma's verspreid over de codebase.
Eenvoudiger testen
Test de hele functie als een eenheid. Je hebt alle stukken op één plek, waardoor integratietesten natuurlijk en eenvoudig worden.
Duidelijkere afhankelijkheden
Wanneer je ziet
import { getAvailability } from '@calcom/features/availability', weet je precies op welke functie je vertrouwt. Wanneer afhankelijkheden te complex worden, is het duidelijk en kan het worden aangepakt.
Repositorypatroon en Dependency Injection
Technologische keuzes mogen niet doorsijpelen door de applicatie. Het Prisma-probleem illustreert dit perfect. We hebben momenteel verwijzingen naar Prisma verspreid over honderden bestanden. Dit creëert enorme koppeling en maakt technologieveranderingen onbetaalbaar duur. We voelen de pijn hiervan nu bij de upgrade van Prisma naar v6.16. Iets dat alleen een lokale refactor achter afgeschermde repositories had moeten zijn, is een meanderende, bijna eindeloze jacht op problemen over meerdere apps geworden.
De standaard vanaf nu:
Alle database-toegang moet via Repository-klassen gaan. We hebben hier al een mooie voorsprong mee.
Repositories zijn de enige code die weet van Prisma (of een andere ORM). Er mag geen logica in zitten.
Repositories worden geïnjecteerd via Dependency Injection containers
Als we ooit overschakelen van Prisma naar Drizzle of een andere ORM, zijn de enige vereiste veranderingen:
Repository-implementaties
DI-containterbekabeling voor nieuwe repositories
Niets anders in de codebase moet zich erom bekommeren of veranderen
Dit is niet theoretisch. Dit is hoe we onderhoudbare systemen bouwen.
Gegevensoverdrachtobjecten (DTO's)
Databasetypen mogen niet lekken naar de frontend. Dit is een populaire korte route geworden in ons techstack, maar het is een geur van code die meerdere problemen creëert.
Technologische koppeling (Prisma-typen eindigen in React-componenten)
Beveiligingsrisico's (onbedoeld lekken van gevoelige velden)
Fragiele contracten tussen server en client (dit is vooral problematisch naarmate we veel meer API's bouwen)
Onvermogen om het databaseschema onafhankelijk te evolueren
Alle DTO-conversies door Zod, zelfs voor een API-reactie om ervoor te zorgen dat alle gegevens worden gevalideerd voordat ze naar de gebruiker worden verzonden. Beter falen dan iets verkeerds terugsturen.
De standaard vanaf nu:
Maak expliciete DTO's bij elke architecturale grens.
Gegevenslaag → Applicatielaag → API: Transformeer databasemodellen in toepassingslaag-DTO's, transformeer vervolgens applicatielaag-DTO's in API-specifieke DTO's
API → Applicatielaag → Gegevenslaag: Transformeer API-DTO's via de applicatielaag en in gegeven-specifieke DTO's
Ja, dit vereist meer code. Ja, het is het waard. Expliciete grenzen voorkomen de architecturale erosie die lange termijn onderhoudsnachtmerries creëert.
Domain-Driven Design Patronen
De volgende patronen moeten correct en consistent worden gebruikt:
Applicatieservices
Orkestreer use cases, coördineer tussen domain diensten en repositories
Domain Diensten
Bevat zakelijke logica die niet van nature tot één entiteit behoort
Repositories
Abstracte gegevens, isoleren technologie keuzes
Dependency Injection
Mogelijk maken van losse koppeling, faciliteren van testen, isoleren van zorgen
Caching Proxy's
Omsluiten repositories of diensten om cachegedrag transparant toe te voegen
Natuurlijk niet de enige manier om caching te doen, maar een mooi startpunt
Decorators
Voeg cross-cutting concerns (logging, metrics, enz.) toe zonder domeinlogica te vervuilen
Consistentie van de Codebase
Onze codebases moeten aanvoelen alsof één persoon ze heeft geschreven. Dit niveau van consistentie vereist strikte naleving van vastgestelde patronen, uitgebreide lintregels die architectonische standaarden afdwingen code reviews die patroon schendingen afwijzen + de hulp van AI code reviewers.
Verplaats Conditionals naar het Toepassingsinvoerpunt
Als-instructies behoren thuis in het invoerpunt, niet verspreid over je services. Dit is een van de belangrijkste architectonische principes voor het behouden van schone, gerichte code die niet onhoudbaar complex wordt.
Dit is hoe code na verloop van tijd achteruitgaat: Een service is geschreven voor een duidelijk, specifiek doel. De logica is schoon en gefocust. Dan komt er een nieuwe productvereiste binnen, en iemand voegt een if-statement toe. Een paar jaar en nog een aantal vereisten later zit die service vol met conditionele controles voor verschillende scenario's. De service is geworden:
Gecompliceerd en moeilijk te lezen
Moeilijk te begrijpen en te doorgronden
Meer vatbaar voor bugs
Overtreding van enkelvoudige verantwoordelijkheid (handelt te veel verschillende gevallen af)
Vrijwel onmogelijk volledig te testen
De service is over zijn grenzen gegaan qua verantwoordelijkheden en logica.
Een Oplossing: Factory-patroon met Gespecialiseerde Services
Gebruik het factory-patroon om beslissingen te maken op het invoerpunt, en draag dan over aan gespecialiseerde services die hun specifieke logica zonder conditionals afhandelen.
Voorbeeld uit onze codebase:
De BillingPortalServiceFactory bepaalt of facturering is voor een organisatie, team of individuele gebruiker, en retourneert dan de juiste service:
Elke service behandelt zijn specifieke logica zonder te hoeven controleren "ben ik een organisatie of een team?":
Waarom Dit Belangrijk Is
Services blijven gefocust
Elke service heeft één verantwoordelijkheid en hoeft niets te weten over andere contexten. De
OrganizationBillingPortalServicebevat geen if-statement checksif (isTeam)ofif (isUser). Het weet alleen hoe het organisaties moet afhandelen.
Wijzigingen zijn geïsoleerd
Wanneer je de organisatie-logica voor facturering moet wijzigen, hoef je alleen maar
OrganizationBillingPortalServiceaan te raken. Je loopt geen risico om team- of gebruikersfacturering te breken. Je hoeft geen geneste conditionals te volgen om te begrijpen welk pad je code volgt.
Testen is eenvoudig
Test elke service onafhankelijk met zijn specifieke scenario's. Geen nood om elke combinatie van conditionals in verschillende contexten te testen.
Nieuwe vereisten vervuilen bestaande code niet
bijv. Wanneer je bedrijf-facturering met andere regels moet toevoegen, creëer je
EnterpriseBillingPortalService. De factory krijgt één voorwaarde meer, maar bestaande services blijven onaangeroerd en gefocust.
Hoe Te Bereiken
Duw conditionals omhoog naar controllers, factories of routeringslogica. Laat deze invoerpunten beslissingen nemen over welke service te gebruiken.
Houd services zuiver en gericht op één verantwoordelijkheid. Als een service moet controleren "welk type ben ik?", heb je waarschijnlijk meerdere services nodig.
Voorkeur voor polymorfisme boven conditionals
Interfaces definiëren het contract. Concrete implementaties bieden de specificaties.
Let op conditieophoping
Tijdens code review, als je ziet dat een service conditionals zich ophoopt voor verschillende scenario's, is dat een signaal om te refactoren naar gespecialiseerde services.
API Design: Dunne Controllers en HTTP Abstractie
Controllers zijn dunne lagen die zich alleen bezighouden met HTTP-zaken.
Ze nemen verzoeken aan, verwerken ze en mappen gegevens naar DTO's die aan kernapplicatielogica worden doorgegeven. Voortaan mag er geen applicatie- of kernlogica te zien zijn in API-routes of tRPC-handlers.
We moeten HTTP-technologie loskoppelen van onze applicatie.
De manier waarop we gegevens tussen client en server overdragen (of het nu REST, tRPC, enz. is) mag de werking van onze kernapplicatie niet beïnvloeden. HTTP is een leveringsmechanisme, geen architectonische driver.
Controller verantwoordelijkheden (en ALLEEN deze):
Ontvangen en valideren van binnenkomende verzoeken
Gegevens uit aanvraagparameters, body, headers halen
Verzoekgegevens omzetten in DTO's
Juist applicatiediensten aanroepen met die DTO's
Applicatieservice-antwoorden omzetten in respons-DTO's
HTTP-antwoorden retourneren met de juiste statuscodes
Controllers moeten NIET:
Bedrijfseigen logica of domeinregels bevatten
Direct toegang krijgen tot databases of externe diensten
Ingewikkelde gegevensomzettingen of berekeningen uitvoeren
Beslissingen nemen over wat de applicatie moet doen
Weten van implementatiedetails van het domein
Voorbeeld van het dunne controller-patroon:
API Versiebeheer en Brekende Veranderingen
Geen brekende veranderingen. Dit is cruciaal. Zodra een API-endpoint openbaar is, moet het stabiel blijven. Brekende veranderingen vernietigen het vertrouwen van ontwikkelaars en creëren integratie nachtmerries voor onze gebruikers.
Strategies voor het vermijden van brekende veranderingen:
Voeg altijd nieuwe velden toe als optioneel
Gebruik API-versiebeheer wanneer u bestaand gedrag moet wijzigen
Deprecieer oude endpoints op een sierlijke manier met duidelijke migratiepaden
Behoud achterwaartse compatibiliteit voor ten minste twee hoofdversies
Wanneer u brekende veranderingen moet maken:
Maak een nieuwe API-versie aan met behulp van het datum specifieke versiebeheer in API v2 (misschien zullen we ook naar het als recent geïntroduceerde noemversiebeheer van Stripe kijken)
Voer beide versies tegelijkertijd uit tijdens de overgang (we doen dit al in API v2)
Bied indien mogelijk geautomatiseerde migratietools aan
Geef gebruikers ruim de tijd om te migreren (minimaal 6 maanden voor openbare API's)
Documenteer precies wat er veranderd is en waarom
Prestatie en Algorithmische Complexiteit
We bouwen voor grote organisaties en teams. Wat prima werkt met 10 gebruikers of 50 records kan onder het gewicht van enterprise schaal instorten. Prestaties zijn geen iets wat we later optimaliseren. Het is iets wat we correct vanaf het begin bouwen.
Denk Aan Schaal Vanaf Dag Eén
Bij het bouwen van functies, vraag altijd: "Hoe gedraagt dit zich met 1.000 gebruikers? 10.000 records? 100.000 operaties?" Het verschil tussen O(n) en O(n²) algoritmen kan onzichtbaar zijn in ontwikkeling, maar rampzalig in productie.
Veelvoorkomende O(n²) patronen om te vermijden:
Geneste array-iteraties (
.mapbinnen.map,.forEachbinnen.forEach)Array-methoden zoals
.some,.find, of.filterbinnen lussen of callbacksElk item vergelijken met elk ander item zonder optimalisatie
Gefilterde filters of geneste mapping over grote lijsten
Reëel wereld voorbeeld: Voor 100 beschikbare slots en 50 drukke periodes, presteert een O(n²) algoritme 5.000 controles. Schaal dit naar 500 slots en 200 drukke periodes, en je doet 100.000 operaties. Dat is een 20x toename in computationele lading voor slechts een 5x toename in gegevens.
Kies de Juiste Gegevensstructuren en Algoritmen
De meeste prestatieproblemen worden opgelost door betere gegevensstructuren en algoritmen te kiezen:
Sorteren + vroege uitgang: Sorteren je gegevens één keer, dan in lussenbreken wanneer je weet dat de rest van de items niet zal overeenkomen
Binaire zoekopdracht: Gebruik binaire zoekopdrachten voor doorzoekingen in gesorteerde arrays in plaats van lineaire scans
Twee-vinger technieken: Voor het samenvoegen of kruisen van gesorteerde sequenties, loop er doorheen met pointers in plaats van geneste loops
Hash-sets: Gebruik objecten of Sets voor O(1) zoekopdrachten in plaats van
.findof.includesop arraysInterval bomen: Voor planning, beschikbaarheid, en bereik aanvragen, gebruik goede boomstructuren in plaats van brute force vergelijkingen
Voorbeeld transformatie:
Geautomatiseerde Prestatietesten
We zullen meerdere lagen van verdediging implementeren tegen prestatieregressies:
Linting regels die vlaggen:
Functies met geneste loops of geneste array-methoden
Meerdere geneste
.some,.find, of.filtercallsRecursie zonder memoization
Bekende anti-patronen voor ons domein (planning, beschikbaarheidscontroles, enz.)
Prestatienormen in CI die:
Cruciale algoritmen uitvoeren op realistische, grootschalige gegevens
Uitvoeringstijden vergelijken met baseline bij elke PR
Samengaan blokkeren die prestatieregressies introduceren
Testen met gegevens op ondernemingsniveau (duizenden gebruikers, tienduizenden records)
Productiebewaking die:
Tracksuitvoeringstijd voor cruciale paden
Waarschuwingen bij vertraging van algoritmen naarmate gegevens groeien
Regressies vastleggen voordat gebruikers iets merken
Reële prestatiedata biedt om optimalisaties te informeren
Prestaties zijn een Kenmerk
Prestaties zijn niet optioneel. Het is niet iets wat we "later" doen. Voor zakelijke klanten die boeken over grote teams, betekenen trage reacties verloren productiviteit en gefrustreerde gebruikers (onze ervaring met sommige grotere zakelijke klanten kan hiervan getuigen).
Elke ingenieur moet:
Voer een profiel van uw code uit voordat u optimaliseert, maar denk vanaf het begin aan complexiteit
Test met realistische, grootschalige gegevens (niet alleen 5 test records). We hebben al zaaiprotocollen gebouwd. We moeten ze waarschijnlijk uitbreiden.
Kies efficiënte algoritmen en gegevensstructuren vooraf
Let op geneste iteraties in code review
Vraag naar elk algoritme dat met het product van twee variabelen schaalt
De NP-Harde Realiteit van Planning
Planproblemen zijn fundamenteel NP-moeilijk. Dit betekent dat naarmate het aantal beperkingen, deelnemers, of tijdslots groeit, de computationele complexiteit exponentieel kan toenemen. De meeste optimale planningsalgoritmen hebben een worst-case exponentiële tijdcomplexiteit, wat keuze van algoritmen cruciaal maakt.
Reële wereldimplicaties:
De optimale vergadertijd voor 10 mensen vinden over 3 tijdzones met afzonderlijke beschikbaarheidsbeperkingen is computationeel duur
Door conflictdetectie, buffers, en een veelheid aan andere opties toe te voegen, wordt het probleem versterkt
Slechte algoritmen die prima werken voor kleine teams worden volledig onbruikbaar voor grote organisaties
Wat milliseconden kost voor 5 gebruikers kan meerdere seconden duren voor ondernemingen
Strategies voor het beheren van NP-moeilijke complexiteit:
Gebruik benaderingsalgoritmen die "goed genoeg" oplossingen snel vinden in plaats van perfecte oplossingen langzaam
Implementeer agressieve caching van berekende schema's en beschikbaarheid
Pre-compute veelvoorkomende scenario's buiten drukke uren
Breek grote planningsproblemen op in kleinere, beter beheersbare stukken
Stel redelijke time-out limieten en val terug naar eenvoudigere algoritmen wanneer nodig
Dit is waarom prestaties niet alleen fijn zijn in planningssoftware. Het is de basis die bepaalt of je systeem kan opschalen naar enterprise-niveau of instort onder real-world gebruikspatronen.
Code Coverage Vereisten
Wereldwijde dekkingstracking
We volgen de algehele dekking van de codebase als een belangrijke metric die na verloop van tijd verbetert. Dit geeft ons inzicht in onze testvolwassenheid en helpt gebieden te identificeren die aandacht behoeven. Het wereldwijde dekkingpercentage wordt prominent weergegeven in onze dashboards.
80%+ dekking voor nieuwe code
Elke PR moet bijna 80%+ testdekking hebben voor de code die het introduceert of aanpast. Dit wordt automatisch afgedwongen in onze CI-pijplijn. Als je 50 regels nieuwe code toevoegt, moeten die 50 regels gedekt zijn door tests. Als je een bestaande functie aanpast, moeten je wijzigingen worden getest. Dit is de algehele testdekking. Unit-testdekking moet bijna 100% zijn, vooral met de mogelijkheid om AI te gebruiken om deze te genereren.
Inspreken tegen het "dekking is niet het hele verhaal" argument: Ja, we weten dat dekking geen perfecte tests garandeert. We weten dat je zinloze tests kunt schrijven die elke regel raken maar geen betekenisvolle test. We weten dat dekking maar één van de vele metrics is. Maar, het is zeker beter om te streven naar een hoog percentage dan geen idee te hebben waar je bent.
Succes Meten
"Snelheid" (geleend van Scrum hoewel we Scrum niet zullen gebruiken)
Voortdurende groei in maandstatistieken (functies, verbeteringen, refactors)
Kwaliteit
Verminder PR-inspanningen die aan fixes worden besteed van de huidige 35% tot 20% of lager eind 2026 (berekend op basis van bestandswijzigingen en toevoegingen/verwijderingen)
Architecturale gezondheid
Metric op patroon naleving, technologie koppeling, grensoverschrijdende schendingen
Review-efficiëntie
Kleinere PR's, snellere reviews, minder feedbackrondes
Applicatie en API uptime
Hoe dicht benaderen we 99,99%?

Begin vandaag nog gratis met Cal.com!
Ervaar naadloze planning en productiviteit zonder verborgen kosten. Meld je in enkele seconden aan en begin vandaag nog met het vereenvoudigen van je planning, geen creditcard vereist!

