Monorepo vs polyrepo: web, mobiel en backend gesynchroniseerd houden
Monorepo versus polyrepo uitgelegd voor teams die web-, mobiele en backend-apps uitrollen. Vergelijk afhankelijkheden, releases en CI-tactieken om snel te blijven.

Het echte probleem: wijzigingen doorvoeren in drie codebases
Teams discussiëren niet over monorepo versus polyrepo omdat ze van Git-filosofie houden. Ze discussiëren omdat een kleine productwijziging verandert in drie afzonderlijke wijzigingen voor web, mobiel en backend, en er onderweg iets breekt.
Wat als eerste breekt is zelden de UI. Meestal is het de onzichtbare lijm: een API-contract dat verandert zonder bijpassende update, een gedeelde bibliotheek die ergens omhooggewerkt wordt maar niet elders, of de buildpipeline die ineens een nieuwe stap nodig heeft. Wanneer één onderdeel eerder live gaat dan de anderen, ervaren gebruikers dat als bugs zoals “de knop staat op web maar de mobiele app zegt niet ondersteund” of “de app blijft laden omdat de backend-respons veranderde.”
Web, mobiel en backend hebben ook verschillende release-klokken. Web kan meerdere keren per dag uitrollen. Backend kan vaak deployen, maar heeft zorgvuldige rollouts nodig. Mobiel is het traagst vanwege app store review en gebruikersupdates die echte vertraging toevoegen. Een “simpele” wijziging zoals het hernoemen van een veld kan je dwingen te plannen rond de langzaamste lane, zelfs als maar één scherm het nodig heeft.
Je betaalt waarschijnlijk een repo-coördinatietoeslag als dit blijft gebeuren:
- Brekende API-wijzigingen worden pas na merge ontdekt.
- Versie-uitlijning hangt af van handmatige herinneringen en spreadsheets.
- Eén feature heeft meerdere gecoördineerde pull requests die op elkaar wachten.
- CI is traag omdat het veel meer bouwt en test dan de wijziging raakte.
- Rollbacks voelen riskant omdat niet duidelijk is welke commit bij welke release hoort.
Teamgrootte en productrijpheid veranderen het juiste antwoord. Vroeg winnen de meeste teams door coördinatie goedkoop en zichtbaarheid hoog te maken, ook al is het een beetje rommelig. Naarmate teams groeien, beginnen grenzen te tellen, maar alleen als interfaces stabiel zijn en eigenaarschap duidelijk is.
Als elke betekenisvolle wijziging in drie plekken moet landen, betaal je die belasting op de een of andere manier. Repo-strategie gaat vooral over hoe je wilt betalen.
Monorepo en polyrepo basics zonder jargon
Een repo is gewoon waar je code leeft, samen met de geschiedenis. Als je web, mobiel en backend hebt, is de keuze eenvoudig: alles bij elkaar houden, of opsplitsen.
Een monorepo is één repository die meerdere apps en vaak gedeelde code bevat. Web, iOS/Android, backend services en gedeelde libraries staan naast elkaar.
Een polyrepo is het tegenovergestelde: elke app (en soms elke service) heeft zijn eigen repository. Gedeelde code wordt meestal een apart package, of teams kopiëren kleine stukken wanneer nodig.
Dagelijks voelt een monorepo meestal zo: code delen is makkelijk, cross-app wijzigingen kunnen in één pull request, en regels zijn consistent. Het nadeel is sociaal: eigenaarschap kan vaag worden tenzij je duidelijke grenzen stelt, en repo-brede checks kunnen streng aanvoelen.
Polyrepos voelen vaak zo: elk team kan onafhankelijk bewegen, repos blijven gefocust en toegangscontrole is soms eenvoudiger. Het nadeel is coördinatie: code delen vergt planning en cross-app wijzigingen worden vaak meerdere pull requests met zorgvuldige timing.
Veel teams eindigen met een hybride aanpak: apps in aparte repos, gedeelde contracten op één plek; of één monorepo met sterke grenzen zodat elk team meestal in zijn eigen gebied blijft.
Als je een platform gebruikt dat backend, web en mobiel genereert vanuit één bron van waarheid, verminder je drift omdat contracten en logica samen leven. AppMaster, bijvoorbeeld, genereert productieklare backend, web en native mobiele apps vanuit één model. Dat lost de realiteit van releases niet op (mobiel blijft trager), maar het kan veel van het “hebben we alle drie geüpdatet?”-werk elimineren.
Afhankelijkheidsbeheer: gedeelde code veilig houden
Gedeelde code is waar teams tijd verliezen, ongeacht repo-indeling. Een kleine wijziging in een gedeelde bibliotheek of API-contract kan web-builds, mobiele releases en backend-deploys op verschillende manieren breken.
Als je libraries deelt (UI-componenten, validatieregels, auth-helpers) kies je tussen één versie voor iedereen of meerdere versies in de tijd.
- Eén versie is eenvoudiger en voorkomt verrassingen “werkt op mijn branch”.
- Meerdere versies laten teams in hun eigen tempo bewegen, maar creëren rommel en maken security-updates lastiger uit te rollen.
API-clients en schema’s verdienen extra zorg. Handmatige updates zijn traag en foutgevoelig. Een beter patroon is het API-schema als bron van waarheid te behandelen en clients ervan te genereren, en die client-code in te checken of in CI te genereren. Het doel is snel falen: als de backend een verplicht veld toevoegt, moet de mobiele client falen tijdens build, niet drie dagen later in QA.
Brekende wijzigingen verspreiden zich wanneer gedrag verandert zonder een veilige overgang. Geef eerst de voorkeur aan additieve veranderingen (nieuwe velden, nieuwe endpoints), en depreceer later. Als je iets moet breken, gebruik versiegedrukte endpoints of een korte compatibiliteitsperiode.
Concreet voorbeeld: de backend hernoemt status naar state. Als web vandaag bijwerkt maar mobiel pas over een week kan releasen, moet de backend die week beide velden accepteren of een adapter uitrollen die het oude veld naar het nieuwe mappt.
Een paar regels die afhankelijkheidsupdates saai houden (op een goede manier):
- Werk op cadence. Kleine wekelijkse updates verslaan grote kwartaal-updates.
- Vereis expliciete goedkeuring voor brekende wijzigingen, plus een korte migratienoot.
- Automatiseer checks: dependency bumps, client-regeneratie en basis contracttests.
- Definieer “klaar” als “web, mobiel en backend builds zijn groen”, niet “mijn repo slaagt”.
Gegenereerde code kan drift verminderen, maar vervangt geen discipline. Je hebt nog steeds één contract, duidelijke deprecations en voorspelbare updates nodig.
Release-coördinatie: web, mobiel en backend op één lijn krijgen
Release-coördinatie is waar repo-strategie theoretisch stopt en praktisch wordt. Als de backend een API-veldnaam verandert, kan de web-app meestal dezelfde dag bijwerken en uitrollen. Mobiele apps zijn anders: app store review en gebruikersupdate-timing kunnen een kleine mismatch in een week aan supporttickets veranderen.
Het praktische doel is simpel: een gebruikersactie moet werken, ongeacht welk deel als eerste update. Dat betekent plannen voor gemengde versies, niet uitgaan van perfecte, gesynchroniseerde releases.
Versiebeheerpatronen die teams daadwerkelijk gebruiken
De meeste teams komen in één van deze aanpakken terecht:
-
Eén gedeelde releasetrein: web, mobiel en backend shippen als één versie-eenheid.
-
Per-service versies met compatibiliteitsregels: elke app/service heeft zijn eigen versie, en de backend ondersteunt een gedefinieerd bereik van clientversies.
Een gedeelde releasetrein ziet er netjes uit, maar valt vaak uit elkaar bij mobiele vertragingen. Per-service versies zijn op papier rommeliger, maar passen bij de realiteit. Als je per-service gaat, noteer en handhaaf één regel: welke backendversies welke mobiele versies moeten ondersteunen, en hoe lang.
Mobiele vertragingen veranderen ook hoe je hotfixes behandelt. Backend-hotfixes kunnen snel uitrollen; mobiele hotfixes bereiken gebruikers mogelijk niet binnen enkele dagen. Geef voorkeur aan server-side fixes die oude mobiele builds blijven ondersteunen. Wanneer je de client moet veranderen, gebruik feature flags en verwijder oude velden niet totdat je weet dat de meeste gebruikers geüpdatet hebben.
Voorbeeld: je voegt “delivery instructions” toe aan een bestelstroom. De backend voegt een optioneel veld toe, web toont het meteen, mobiel toont het in de volgende sprint. Als de backend oude verzoeken accepteert en het veld optioneel houdt, blijft alles werken terwijl mobiel bijwerkt.
Wie beheert de release-kalender
Coördinatie faalt wanneer “iedereen het bezit”, dus niemand het doet. De eigenaar kan een tech lead, een release manager of een productmanager met sterke engineering-ondersteuning zijn. Hun taak is verrassingen te voorkomen door releaseverwachtingen zichtbaar en consistent te houden.
Ze hebben geen complex proces nodig. Wel een paar herhaalbare gewoonten: een simpele release-kalender met cutoffs en freeze-windows, een korte cross-team check voordat API-wijzigingen uitrollen, en een duidelijk plan voor wat er gebeurt als mobiel vertraagd is (backend vasthouden of compatibiliteit behouden).
Als je workflow backend, web en mobiel uit één model genereert, heb je nog steeds een release-eigenaar nodig. Je krijgt meestal minder “hebben we alle drie bijgewerkt?”-momenten, maar je ontloopt de mobiele timing niet.
CI-tijd onder controle houden
CI wordt traag om dezelfde redenen in beide setups: je bouwt te veel, je installeert afhankelijkheden herhaaldelijk en je draait elke test bij elke wijziging.
Veelvoorkomende tijdvreters zijn volledige builds bij kleine wijzigingen, ontbrekende caches, testsuites die alles draaien en seriële jobs die parallel hadden gekund.
Begin met verbeteringen die overal helpen:
- Cache dependency-downloads en build-outputs.
- Draai lint, unittests en builds parallel waar mogelijk.
- Splits snelle checks (elke commit) van langzamere checks (main branch, nachtelijk of pre-release).
Monorepo-tactieken die helpen
Monorepos worden pijnlijk wanneer elke commit een “bouw de wereld”-pipeline triggert. De oplossing is alleen te bouwen en testen wat door de wijziging wordt geraakt.
Gebruik padfilters en een affected-only-benadering: als je mobiele UI-code veranderde, bouw dan niet de backend-images. Als je een gedeelde library aanraakte, bouw en test alleen de apps die ervan afhankelijk zijn. Veel teams formaliseren dit met een eenvoudige afhankelijkheidsgraf zodat CI kan beslissen in plaats van gokken.
Polyrepo-tactieken die drift voorkomen
Polyrepos kunnen snel zijn omdat elke repo kleiner is, maar ze verspillen vaak tijd door duplicatie en inconsistent tooling.
Houd één gedeelde set CI-templates (zelfde stappen, dezelfde caches, dezelfde conventies) zodat niet elk repo de pipeline opnieuw uitvindt. Pin toolchains (runtime-versies, build-tools, linters) om “werkt in één repo” verrassingen te vermijden. Als dependency-downloads een bottleneck zijn, zet gedeelde caches of interne mirrors op zodat niet elke repo de hele wereld opnieuw hoeft te halen.
Concreet voorbeeld: een feature voegt een nieuw “status”-veld toe. Backend verandert, web toont het, mobiel toont het. In een monorepo zou CI backendtests plus alleen de web- en mobile-delen die de API-client gebruiken draaien. In een polyrepo-setup draait elk repo zijn eigen snelle checks, en kan een aparte integratiepipeline valideren dat de drie releases nog overeenkomen.
Als je source code exporteert en je eigen CI draait, geldt dezelfde regel: bouw alleen wat veranderde, hergebruik caches agressief en reserveer langzame checks voor wanneer ze echte waarde toevoegen.
Stap voor stap: kies een repo-strategie die bij je team past
De beslissing wordt makkelijker als je start vanuit dagelijkse werkzaamheden in plaats van ideologie.
1) Schrijf op wat samen moet veranderen
Kies 5 tot 10 recente features en noteer wat in lockstep moest bewegen. Markeer of elk item UI-schermen, API-endpoints, datatabellen, authenticatieregels of gedeelde validatie raakte. Als de meeste features coördinatie over alle drie gebieden vereisen, voelt een gesplitste opzet pijnlijk aan tenzij je releaseproces zeer gedisciplineerd is.
2) Traceer gedeelde code en gedeelde beslissingen
Gedeelde code is niet alleen libraries. Het zijn ook contracten (API-schema’s), UI-patronen en business rules. Noteer waar die nu leven, wie ze bewerkt en hoe wijzigingen worden goedgekeurd. Als gedeelde stukken tussen repos gekopieerd worden, is dat een teken dat je strakkere controle nodig hebt, ofwel door een monorepo of door strikte versieregels.
3) Definieer grenzen en eigenaren
Bepaal wat de eenheden zijn (apps, services, libraries) en wijs een eigenaar toe aan elke eenheid. Grenzen zijn belangrijker dan repo-layout. Zonder eigenaren wordt een monorepo lawaaierig. Zonder eigenaren wordt een polyrepo losgekoppeld.
Als je een eenvoudige checklist wilt: één repo of map per deployable service/app, één plek voor gedeelde contracten, één plek voor echt gedeelde UI-componenten, een duidelijke regel voor waar businesslogica leeft en een gedocumenteerde eigenaar voor elk onderdeel.
4) Kies een releasemodel dat je kunt volgen
Als mobiele releases achterlopen op backend-wijzigingen, heb je een compatibiliteitsplan nodig (geversioneerde API’s, achterwaarts compatibele velden of een gedefinieerd support-window). Als alles samen moet shippen, kan een releasetrein werken, maar het vergroot coördinatie.
Houd branching-regels saai: kortdurende branches, kleine merges en een duidelijk hotfix-pad.
5) Ontwerp CI rond de meest voorkomende wijzigingen
Ontwerp CI niet vanuit het slechtste geval op dag één. Ontwerp het voor wat mensen iedere dag doen.
Als de meeste commits alleen web UI raken, voer dan standaard web-lint en unittests uit en draai volledige end-to-end tests op schema of vóór releases. Als de meeste incidenten van API-drift komen, investeer eerst in contracttests en clientgeneratie.
Voorbeeld: één feature die web, mobiel en backend raakt
Stel je een klein team voor dat drie dingen tegelijk bouwt: een klantenportaal (web), een field-app (mobiel) en een API (backend). Er komt een verzoek: voeg een nieuw “Service status”-veld toe aan jobs en toon het overal.
De wijziging klinkt klein, maar het is een coördinatietest. Backend voegt het veld toe en werkt validatie en responses bij. Web toont het en werkt filters bij. Mobiel moet het offline tonen, synchroniseren en randgevallen afhandelen.
Het echte probleem: de API-wijziging is brekend. De veldnaam verandert van status naar service_status en oude clients crashen als ze er niet mee omgaan.
Wat een monorepo verandert
Hier voelt een monorepo vaak rustiger. Backend-, web- en mobile-updates kunnen in één pull request landen (of één gecoördineerde set commits). CI kan de getroffen tests draaien en je kunt één release taggen die alle drie updates bevat.
Het grootste risico is sociaal, niet technisch: één repo maakt het makkelijk om een brekende wijziging snel te mergen, dus je reviewregels moeten sterk zijn.
Wat een polyrepo verandert
Met losse repos leeft elke app op zijn eigen schema. De backend kan als eerste shippen en web en mobiel haasten om bij te werken. Als mobiele releases app store review nodig hebben, kan de “fix” dagen duren, ook al is de codewijziging klein.
Teams lossen dit gewoonlijk op met meer structuur: geversioneerde endpoints, achterwaarts compatibele responses, langere deprecatievensters en duidelijke rollout-stappen. Het werkt, maar het is blijvend werk.
Bij het kiezen op basis van bewijs, kijk naar de afgelopen maanden:
- Als incidenten vaak door mismatched versies komen, geef dan de voorkeur aan strakkere coördinatie.
- Als releases frequent en tijdkritisch zijn (vooral mobiel), vermijd brekende wijzigingen of centraliseer ze.
- Als teams onafhankelijk zijn en zelden dezelfde feature aanraken, kan de overhead van polyrepo de moeite waard zijn.
Veelgemaakte fouten en valkuilen om te vermijden
De meeste teams falen niet omdat ze de “verkeerde” repo-structuur kozen. Ze falen omdat dagelijkse gewoonten langzaam frictie toevoegen totdat elke wijziging riskant voelt.
Gedeelde code wordt een dumpplaats
Een gedeelde library is verleidelijk: helpers, types, UI-stukken, “tijdelijke” workarounds. Al snel wordt het de plek waar oude code verdwijnt en niemand weet wat veilig te veranderen is.
Houd gedeelde code klein en strikt. “Gedeeld” moet betekenen: door veel teams gebruikt, zorgvuldig beoordeeld en met intentie veranderd.
Strakke koppeling via verborgen aannames
Zelfs in aparte repos kunnen systemen sterk gekoppeld zijn. De koppeling verschuift naar aannames: datumformaten, enum-waarden, permissieregels en “dit veld is altijd aanwezig”.
Voorkom dit door contracten te documenteren (wat velden betekenen, welke waarden zijn toegestaan) en behandel ze als productregels, niet als trivia.
Onduidelijk eigenaarschap
Als iedereen alles kan veranderen, worden reviews oppervlakkig en glippen fouten erdoor. Als niemand eigenaar is, blijven bugs weken liggen.
Definieer eigenaren voor web, mobiel, backend en gedeelde modules. Eigenaarschap blokkeert bijdragen niet; het zorgt dat wijzigingen de juiste ogen krijgen.
CI groeit zonder snoeien
CI begint vaak klein en elke incident voegt een nieuwe job toe “voor de zekerheid”. Maanden later is het traag en duur en vermijden mensen het.
Een simpele regel helpt: elke CI-job heeft een duidelijk doel en een eigenaar, en moet verwijderd worden als hij geen echte problemen meer vangt.
Waarschuwingssignalen dat opschoning nodig is: dubbele tests over jobs heen, jobs die dagenlang rood blijven, “snelle wijzigingen” die langer duren te verifiëren dan te bouwen, en pipelines die mobiele builds triggeren op backend-only wijzigingen.
Release-coördinatie berust op tribal knowledge
Als releases afhangen van één persoon die de juiste volgorde en geheime valkuilen onthoudt, ship je langzamer en breek je vaker dingen.
Schrijf de releasestappen op, maak ze herhaalbaar en automatiseer saaie checks. Zelfs als je tooling consistente backends en clients genereert, heb je nog steeds duidelijke releaseregels nodig.
Snelle checks voordat je commit naar monorepo of polyrepo
Voordat je repos herstructureert, toets de realiteit van hoe je team nu shipt. Het doel is niet een perfecte structuur, maar minder verrassingen wanneer één wijziging web, mobiel en backend raakt.
Stel vijf vragen:
- Onafhankelijk uitrollen: Kun je een backend-fix uitrollen zonder op dezelfde dag een mobiele app-update te forceren?
- API-wijzigingsregels: Heb je een geschreven contract voor deprecations en hoe lang oud gedrag ondersteund blijft?
- Discipline rond gedeelde code: Worden gedeelde libraries (UI-componenten, API-clients, business rules) consequent beoordeeld en versioneerd?
- CI draait wat telt: Kan CI bepalen wat veranderde en alleen builds/tests uitvoeren voor de getroffen delen?
- Één releasetabel: Is er één plek om te zien wat er uitgaat over web, mobiel en backend, met eigenaren en datums?
Eenvoudig voorbeeld: een nieuw “adres”-veld wordt toegevoegd aan checkout. Als de backend eerst uitrolt, moet de oude mobiele app nog werken. Dat betekent meestal dat de API tijdelijk zowel oude als nieuwe payloads accepteert en client-updates optioneel zijn, niet verplicht.
Volgende stappen: minder coördinatiekosten en vol vertrouwen uitrollen
Het doel is niet de “juiste” repo-structuur. Het doel is minder overdrachten, minder verrassingen en minder “wacht, welke versie draait?”-momenten.
Schrijf een korte beslisnotitie: waarom je de huidige aanpak koos, wat je verwacht te verbeteren en welke concessies je accepteert. Herzie het elke 6–12 maanden, of eerder als teamgrootte of release-cadans verandert.
Voordat je bestanden verplaatst, kies de kleinste wijziging die echte pijn wegneemt:
- Voeg en volg versie-regels voor gedeelde packages.
- Definieer API-contracten en handhaaf ze met contracttests in CI.
- Spreek één release-checklist af voor web, mobiel en backend.
- Gebruik preview-omgevingen voor wijzigingen die meerdere onderdelen raken.
- Stel CI-tijdbudgetten in (bijv. PR-checks onder 15 minuten).
Als cross-codebase koppeling de echte bottleneck is, kan het verminderen van het aantal plekken dat moet veranderen belangrijker zijn dan repo-layout. Sommige teams doen dat door meer logica en datamodellering naar één bron van waarheid te verplaatsen.
Als je die aanpak wilt verkennen, is AppMaster (appmaster.io) gebouwd om backend-services, webapps en native mobiele apps te genereren met gedeelde datamodellen en businesslogica. Een laag-risico manier om het te evalueren is met één kleine interne tool, en dan beslissen hoeveel coördinatiewerk het verwijdert.
De zelfverzekerde weg is opzettelijk saai: documenteer de beslissing, verminder koppeling, automatiseer checks en verander repo-structuur alleen wanneer cijfers zeggen dat het helpt.
FAQ
Begin bij hoe vaak één feature daadwerkelijk tegelijk wijzigingen in web, mobiel en backend vereist. Als het grootste deel van het werk dwarsdoorsnijdend is en coördinatie je grootste pijnpunt is, vermindert een monorepo of een sterke "single contract"-aanpak vaak fouten. Als teams zelden hetzelfde raken en onafhankelijk willen werken en releasen, werkt een polyrepo goed mits strikte compatibiliteitsregels.
Meestal komt het door API-drift, mismatches van gedeelde bibliotheekversies en verschillen in releasetiming (vooral vertragingen door app stores). Los het op door rekening te houden met gemengde versies in de praktijk en maak brekende wijzigingen zeldzaam en weloverwogen.
Behandel het API-schema als de bron van waarheid en genereer clients ervan, zodat mismatches tijdens de build falen in plaats van in QA of productie. Geef voorkeur aan toegevoegde (additive) veranderingen eerst, depreceer oude velden later, en houd een korte compatibiliteitsperiode bij het hernoemen of verwijderen van iets.
Streef naar kleine, regelmatige updates volgens een cadence (bijvoorbeeld wekelijks in plaats van elk kwartaal) en eis expliciete goedkeuring voor brekende wijzigingen. Voeg bij elke wijziging een korte migratie-opmerking toe en definieer “klaar” als “web, mobiel en backend builds zijn groen”, niet alleen één repo.
Standaard is per-service versiebeheer met een duidelijke compatibiliteitsregel: welke backendversies welke clientversies ondersteunen en hoe lang. Een gedeelde release train kan vroeg werken, maar mobiele vertragingen maken dat vaak lastig tenzij je product kan wachten op app store timing.
Houd de backend compatible zodat oude mobiele builds blijven werken terwijl gebruikers updaten. Gebruik additieve velden, verwijder oud gedrag niet te vroeg en gebruik feature flags bij gefaseerde uitrol van clientzichtbare wijzigingen.
Maak het expliciet iemands taak—vaak een tech lead, release manager of producteigenaar met engineering-ondersteuning. Het doel is een eenvoudige, herhaalbare kalender en een duidelijke beslisregel voor vertragingen (houd een wijziging tegen versus behoud compatibiliteit), geen zwaar proces.
Bouw en test alleen wat veranderde en cache alles wat mogelijk is. Splits snelle controles (bij elke commit) van langzamere checks (main branch, nachtelijke runs of pre-releases) zodat ontwikkelaars snel feedback krijgen zonder steeds de volledige testkosten te betalen.
Gebruik padfilters en een “affected-only”-benadering zodat je niet de hele wereld bouwt voor een kleine wijziging. Als een gedeelde module verandert, run alleen checks voor de apps die ervan afhankelijk zijn, en hou ownership- en reviewregels duidelijk zodat één repo niet ieders rommelbak wordt.
Standaardiseer tooling en CI-templates over repos heen zodat elk repository niet zijn eigen stappen, caches en conventies hoeft uit te vinden. Voeg een integratiecheck toe die sleutelcontracten over releases valideert en pin toolchain-versies om verrassingen te vermijden.


