[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"technologies":3,"\u002Fblog\u002Fimpossible-states-dart-sealed-classes":167,"team-members":2192,"blog-topics":2534,"mdc--a70y1b-key":2575,"glossary":2693},[4,10,15,21,26,32,38,43,48,53,58,63,68,73,77,82,87,92,97,103,108,113,118,123,128,133,138,143,147,152,157,162],{"title":5,"description":6,"slug":7,"category":8,"icon":9},"Android","Elke app die we uitbrengen bereikt Android — platform-API's, uitvoering op de achtergrond, permissies en de winkeleisen die daarbij horen.","android","framework","logos:android-icon",{"title":11,"description":12,"slug":13,"category":8,"icon":14},"Angular","Het front-endframework waarin we werken als een product al Angular is — een kant-en-klare structuur die past bij grote, langlevende applicaties.","angular","logos:angular-icon",{"title":16,"description":17,"slug":18,"category":19,"icon":20},"Dart","De taal waarin elke Flutter-app die we uitbrengen geschreven is — sound null safety, echte pattern matching, en een compiler voor native en web.","dart","language","logos:dart",{"title":22,"description":23,"slug":24,"category":8,"icon":25},"Django","Het Python-framework dat we pakken als een product op dag één een beheeromgeving, authenticatie en een echt datamodel nodig heeft.","django","logos:django-icon",{"title":27,"description":28,"slug":29,"category":30,"icon":31},"Docker","Elke service die we bouwen verschijnt als container, zodat wat op een laptop, in CI en in productie draait één artefact is en niet drie verschillende.","docker","infrastructure","logos:docker-icon",{"title":33,"description":34,"slug":35,"category":36,"icon":37},"Fastlane","De releaseautomatisering achter ons mobiele werk — ondertekenen, builds en winkeluploads draaien vanuit CI, niet vanaf één laptop.","fastlane","tools","logos:fastlane",{"title":39,"description":40,"slug":41,"category":8,"icon":42},"Flutter","Onze belangrijkste mobiele stack sinds 2018 — één Dart-codebase die uitkomt op iOS, Android, web en desktop zonder een apart team per platform.","flutter","logos:flutter",{"title":44,"description":45,"slug":46,"category":36,"icon":47},"Git","Elk project dat we aanraken leeft in Git — beoordeelde pull requests, CI op elke branch, en een historie die maanden later nog te volgen is.","git","logos:git-icon",{"title":49,"description":50,"slug":51,"category":19,"icon":52},"Go","Onze backendtaal voor realtime-API's en services onder belasting — kleine binaries, snelle builds, en gelijktijdigheid die leesbaar blijft.","go","logos:gopher",{"title":54,"description":55,"slug":56,"category":30,"icon":57},"Google Cloud","De cloud waarop onze productieworkloads draaien — beheerde Kubernetes, opslag en netwerken, zonder handgebouwde servers die niemand wil onderhouden.","gcp","logos:google-cloud",{"title":59,"description":60,"slug":61,"category":36,"icon":62},"Gradle","Het buildsysteem waar elke Android-release doorheen gaat — product flavours, ondertekeningsconfiguraties, en de afhankelijkheden onder een Flutter-app.","gradle","logos:gradle",{"title":64,"description":65,"slug":66,"category":30,"icon":67},"Helm","Hoe we een Kubernetes-uitrol verpakken — service, configuratie, secrets en ingress als één versiebeheerde eenheid die je kunt promoveren en terugdraaien.","helm","logos:helm",{"title":69,"description":70,"slug":71,"category":19,"icon":72},"Kotlin","Waar we naar grijpen als een Flutter-app echt Android eronder nodig heeft — platform channels, achtergrondwerk en integraties met native SDK's.","kotlin","logos:kotlin-icon",{"title":74,"description":75,"slug":76,"category":8,"icon":72},"Kotlin Multiplatform","Bedrijfslogica delen tussen iOS en Android terwijl elk platform zijn eigen native UI houdt — het alternatief wanneer Flutter niet past.","kmp",{"title":78,"description":79,"slug":80,"category":30,"icon":81},"Kubernetes","Hoe we services in productie draaien — Helm-uitrollen, rollouts die terug te draaien zijn, en schalen zonder dat iemand om 3 uur 's nachts wakker hoeft te zijn.","kubernetes","logos:kubernetes",{"title":83,"description":84,"slug":85,"category":30,"icon":86},"NATS","Lichtgewicht berichtenverkeer tussen services — publish\u002Fsubscribe en request\u002Freply zonder het beheergewicht van een volledig brokercluster.","nats","logos:nats-icon",{"title":88,"description":89,"slug":90,"category":8,"icon":91},"Nuxt","Vue met serverrendering, routering en SEO al geregeld — hoe we marketingsites en webapps bouwen die bij het eerste laden snel en vindbaar moeten zijn.","nuxt","logos:nuxt-icon",{"title":93,"description":94,"slug":95,"category":19,"icon":96},"PHP","Waar we met een bestaande PHP-backend werken — hem uitbreiden, ermee integreren, en de mobiele en webclients bouwen die hij moet bedienen.","php","logos:php",{"title":98,"description":99,"slug":100,"category":101,"icon":102},"PostgreSQL","Onze standaarddatabase — waar we naar grijpen tenzij een product een specifieke reden geeft om dat niet te doen, van schema tot indexen.","postgres","database","logos:postgresql",{"title":104,"description":105,"slug":106,"category":19,"icon":107},"Python","Onze taal voor backends, datawerk en AI-integraties — inclusief de Python-bindings die we bij onze eigen Rust-tooling leveren.","python","logos:python",{"title":109,"description":110,"slug":111,"category":8,"icon":112},"React","De front-endbibliotheek waarin we werken als een product al React is — componenten, hooks, en het ecosysteem dat eromheen is gegroeid.","react","logos:react",{"title":114,"description":115,"slug":116,"category":101,"icon":117},"Redis","Waar we data neerzetten die snel moet zijn en opnieuw op te bouwen valt — caches, sessies, verzoeklimieten en achtergrondwachtrijen.","redis","logos:redis",{"title":119,"description":120,"slug":121,"category":19,"icon":122},"Ruby","De taal waarin onze mobiele releaseautomatisering geschreven is — Fastlane-lanes, eigen acties, en de CI-lijm die builds naar de winkels brengt.","ruby","logos:ruby",{"title":124,"description":125,"slug":126,"category":19,"icon":127},"Rust","Waar we heen gaan als prestaties én correctheid allebei tellen — documenten renderen, CLI-tooling, en services die snel en voorspelbaar moeten blijven.","rust","simple-icons:rust",{"title":129,"description":130,"slug":131,"category":101,"icon":132},"SQLite","De database die meereist in de app — lokale caches, offline-first opslag, en alles wat ook zonder netwerk moet blijven werken.","sqlite","logos:sqlite",{"title":134,"description":135,"slug":136,"category":8,"icon":137},"Strapi","Een headless CMS waar we naar grijpen als redacteuren de content moeten beheren — een echte beheeromgeving en een schone API, zonder ze zelf te bouwen.","strapi","logos:strapi-icon",{"title":139,"description":140,"slug":141,"category":19,"icon":142},"Swift","Waar we naar grijpen als een Flutter-app echt iOS eronder nodig heeft — platform channels, integraties met native SDK's, widgets en App Clips.","swift","logos:swift",{"title":144,"description":145,"slug":146,"category":8,"icon":142},"SwiftUI","Het UI-framework van Apple, waar we de native oppervlakken bouwen die een Flutter-app niet kan afdekken — widgets, App Clips, extensies en systeemintegraties.","swiftui",{"title":148,"description":149,"slug":150,"category":8,"icon":151},"Tailwind CSS","Hoe we elke front-end die we bouwen vormgeven — utility-klassen en designtokens in plaats van een stylesheet die alleen maar groeit en nooit wordt opgeschoond.","tailwind","logos:tailwindcss-icon",{"title":153,"description":154,"slug":155,"category":19,"icon":156},"TypeScript","De standaard voor alles wat we voor de browser en voor Node-services schrijven — types die integratiebugs vangen voordat ze een review halen.","typescript","logos:typescript-icon",{"title":158,"description":159,"slug":160,"category":8,"icon":161},"Vue","Ons front-endframework voor beheerpanelen, dashboards voor winkeliers en webapps die jaren onderhoudbaar moeten blijven, geen sprints.","vue","logos:vue",{"title":163,"description":164,"slug":165,"category":36,"icon":166},"Whisper","Het opensource spraakherkenningsmodel van OpenAI — de transcriptiemotor achter spraakinvoer, draaiend als backendservice én op het toestel in de app.","whisper","simple-icons:openai",{"id":168,"title":169,"author":170,"body":171,"description":177,"extension":2175,"hero":2176,"meta":2179,"metaDescription":2180,"metaTitle":2181,"navigation":800,"ogDescription":2182,"path":2183,"seo":2184,"slug":2185,"stem":2186,"summary":2187,"timestamp":2188,"topic":2189,"updated":2190,"__hash__":2191},"blog_nl\u002Fblog\u002Fimpossible-states-dart-sealed-classes.md","Vijf states die niet kunnen bestaan, en jouw code staat ze alle vijf toe","nixan",{"type":172,"value":173,"toc":2154},"minimark",[174,178,181,270,273,281,432,438,443,510,514,517,522,534,537,542,545,552,574,584,587,645,651,657,664,671,675,681,687,698,705,711,715,722,725,741,755,761,765,771,857,868,887,901,916,923,927,930,936,941,1127,1150,1157,1171,1176,1292,1309,1313,1320,1337,1341,1344,1349,1458,1467,1475,1526,1540,1545,1600,1606,1633,1640,1643,1677,1690,1693,1697,1700,1734,1737,1745,1752,1762,1765,1768,1775,1779,1835,1838,1841,1849,1852,1856,1859,1872,1901,1904,1990,1997,2004,2021,2027,2048,2067,2074,2078,2085,2091,2095,2098,2101,2104,2107,2111,2115,2131,2134,2150],[175,176,177],"p",{},"Hier is een taak waarvan je twintig versies hebt geschreven: een scherm met een productlijst. Ophalen van de server, de fout afhandelen, de lijst tonen.",[175,179,180],{},"We gaven hem aan een agent — Claude Opus 5 — en dit kwam terug.",[182,183,187],"pre",{"className":184,"code":185,"language":18,"meta":186,"style":186},"language-dart shiki shiki-themes material-theme-lighter github-light github-dark","class ProductsState {\n  final List\u003CProduct>? items;\n  final bool isLoading;\n  final Exception? error;\n}\n","",[188,189,190,207,236,249,264],"code",{"__ignoreMap":186},[191,192,195,199,203],"span",{"class":193,"line":194},"line",1,[191,196,198],{"class":197},"smGrS","class",[191,200,202],{"class":201},"sZMiF"," ProductsState",[191,204,206],{"class":205},"su5hD"," {\n",[191,208,210,214,217,220,223,226,229,232],{"class":193,"line":209},2,[191,211,213],{"class":212},"sbsja","  final",[191,215,216],{"class":201}," List",[191,218,219],{"class":205},"\u003C",[191,221,222],{"class":201},"Product",[191,224,225],{"class":205},">",[191,227,228],{"class":197},"?",[191,230,231],{"class":205}," items",[191,233,235],{"class":234},"sP7_E",";\n",[191,237,239,241,244,247],{"class":193,"line":238},3,[191,240,213],{"class":212},[191,242,243],{"class":201}," bool",[191,245,246],{"class":205}," isLoading",[191,248,235],{"class":234},[191,250,252,254,257,259,262],{"class":193,"line":251},4,[191,253,213],{"class":212},[191,255,256],{"class":201}," Exception",[191,258,228],{"class":197},[191,260,261],{"class":205}," error",[191,263,235],{"class":234},[191,265,267],{"class":193,"line":266},5,[191,268,269],{"class":205},"}\n",[175,271,272],{},"Dat ziet eruit als iets wat je zelf zou schrijven. Dat is precies het punt: deze modellen zijn getraind op wat wij allemaal al jaren met de hand schrijven.",[175,274,275,276,280],{},"Tel maar. Drie velden: een bool, een nullable lijst en een nullable fout. Per veld twee toestanden die ertoe doen. Twee keer twee keer twee — ",[277,278,279],"strong",{},"acht combinaties."," Hoeveel daarvan betekenen iets?",[282,283,284,309],"table",{},[285,286,287],"thead",{},[288,289,290,296,301,306],"tr",{},[291,292,293],"th",{},[188,294,295],{},"isLoading",[291,297,298],{},[188,299,300],{},"items",[291,302,303],{},[188,304,305],{},"error",[291,307,308],{},"Wat het betekent",[310,311,312,332,349,364,378,392,404,416],"tbody",{},[288,313,314,320,325,329],{},[315,316,317],"td",{},[188,318,319],{},"true",[315,321,322],{},[188,323,324],{},"null",[315,326,327],{},[188,328,324],{},[315,330,331],{},"Laden",[288,333,334,339,343,346],{},[315,335,336],{},[188,337,338],{},"false",[315,340,341],{},[188,342,324],{},[315,344,345],{},"gevuld",[315,347,348],{},"Mislukt",[288,350,351,355,357,361],{},[315,352,353],{},[188,354,338],{},[315,356,345],{},[315,358,359],{},[188,360,324],{},[315,362,363],{},"Geladen",[288,365,366,370,372,376],{},[315,367,368],{},[188,369,319],{},[315,371,345],{},[315,373,374],{},[188,375,324],{},[315,377,228],{},[288,379,380,384,388,390],{},[315,381,382],{},[188,383,319],{},[315,385,386],{},[188,387,324],{},[315,389,345],{},[315,391,228],{},[288,393,394,398,400,402],{},[315,395,396],{},[188,397,319],{},[315,399,345],{},[315,401,345],{},[315,403,228],{},[288,405,406,410,412,414],{},[315,407,408],{},[188,409,338],{},[315,411,345],{},[315,413,345],{},[315,415,228],{},[288,417,418,422,426,430],{},[315,419,420],{},[188,421,338],{},[315,423,424],{},[188,425,324],{},[315,427,428],{},[188,429,324],{},[315,431,228],{},[175,433,434,435,437],{},"Drie. De andere vijf zijn geen compileerfouten. Het is geldige code. Het compileert, het komt door de review, het gaat live en het blijft daar liggen. ",[188,436,295],{}," staat op true en er ligt een fout naast — wat render je? Of alles is leeg: niet aan het laden, geen lijst, geen fout. Zijn we nog niet begonnen, of zijn we gegaan en met niets teruggekomen? De code weet het niet. Hij gokt, of de volgende die het bestand opent gokt.",[439,440,442],"h2",{"id":441},"kernpunten","Kernpunten",[444,445,446,453,459,469,478,488,494,504],"ul",{},[447,448,449,452],"li",{},[277,450,451],{},"Een onmogelijke state is er een die je type toelaat en je domein verbiedt."," Het is hetzelfde als een gebroken invariant, van de andere kant bekeken.",[447,454,455,458],{},[277,456,457],{},"De kosten zijn geen runtimebugs."," Het gaat om een afspraak die niemand heeft opgeschreven, om takken die niemand ooit heeft overwogen, en om een belasting die je bij elke leesbeurt betaalt in plaats van één keer per incident.",[447,460,461,464,465,468],{},[277,462,463],{},"De invariant bestaat altijd."," De enige vraag is of een mens hem vasthoudt — een commentaar, een wikipagina, een ",[188,466,467],{},"assert"," die in release niet draait — of dat er niets vast te houden valt omdat je hem niet kúnt breken: dat compileert niet.",[447,470,471,477],{},[277,472,473,476],{},[188,474,475],{},"enum Status"," plus nullable velden maakt het erger",", niet beter: twaalf combinaties in plaats van acht, en nog steeds drie die iets betekenen.",[447,479,480,483,484,487],{},[277,481,482],{},"Een sealed class met niet-nullable velden houdt precies de zinvolle states over"," en maakt de rest onbouwbaar. Geen booleans, geen nullables, geen ",[188,485,486],{},"!",".",[447,489,490,493],{},[277,491,492],{},"Volledigheidscontrole is de opbrengst."," Voeg een vierde state toe en de compiler somt elke plek op die hem niet afhandelt — dat is feedback waar een agent iets mee kan, geen muur waar hij tegenaan loopt.",[447,495,496,503],{},[277,497,498,499,502],{},"Eén ",[188,500,501],{},"_ =>","-tak zet dat allemaal uit, stilzwijgend."," Dat is de enige reviewregel die je uit dit artikel hoeft mee te nemen.",[447,505,506,509],{},[277,507,508],{},"Algebraïsche datatypes repareren niet alles."," Ze elimineren onmogelijke combinaties van states, niet onmogelijke waarden, en op een systeemgrens doen ze niets.",[439,511,513],{"id":512},"onmogelijke-states-en-hun-oudere-naam","Onmogelijke states, en hun oudere naam",[175,515,516],{},"Eerst de terminologie, want er zijn twee woorden voor één ding.",[175,518,519,521],{},[277,520,451],{}," Een scherm kan niet tegelijk aan het laden zijn en een fout tonen. Het type staat het toe. Dat is een gat.",[175,523,524,525,528,529,533],{},"De oudere en preciezere naam is ",[277,526,527],{},"invariant"," — een regel die te allen tijde moet gelden. ",[530,531,532],"em",{},"Als we laden, is er nog geen data. Als er een fout is, heeft die een bericht."," Een onmogelijke state is simpelweg een gebroken invariant. Hetzelfde feit van twee kanten: de invariant is hoe het hoort te zijn, de onmogelijke state is wat je krijgt als het dat niet is.",[175,535,536],{},"Onthoud dat woord. Daar draait het om.",[538,539,541],"h3",{"id":540},"waarom-dit-een-probleem-is-en-waarom-de-gebruikelijke-uitleg-niet-klopt","Waarom dit een probleem is, en waarom de gebruikelijke uitleg niet klopt",[175,543,544],{},"Het gebruikelijke verhaal luidt dat onmogelijke states bugs zijn die niet crashen: ze renderen stilzwijgend het verkeerde scherm. Dat klinkt overtuigend, en als dát het probleem was, zou het antwoord goedkoop zijn: tests, QA, een alert op het lege scherm. Vangen, fixen, door.",[175,546,547,548,551],{},"Dat is het probleem niet. Het gaat helemaal niet over runtime. Er zijn drie kostenposten, en alle drie zitten ",[277,549,550],{},"op dit moment"," in je code, ook als de app zich perfect gedraagt.",[175,553,554,557,558,560,561,563,564,566,567,569,570,573],{},[277,555,556],{},"Eén: de afspraak die niemand opschreef."," Kijk nog eens naar de klasse van de agent. Waar staat dat je ",[188,559,300],{}," niet moet lezen als ",[188,562,295],{}," true is? Nergens. Waar staat dat ",[188,565,305],{}," en ",[188,568,300],{}," nooit allebei gevuld zijn? Nergens. Die regels bestaan en mensen vertrouwen erop, maar ze staan niet in het type en niet in een commentaar. Ze leven in het hoofd van wie de klasse schreef, en iedereen daarna reconstrueert ze uit de code, door de ",[188,571,572],{},"if","s te lezen en de bedoeling af te leiden. Vijf minuten per bestand, voor iedereen die het opent.",[175,575,576,579,580,583],{},[277,577,578],{},"Twee: het aantal takken."," Acht combinaties betekenen acht gevallen, en met elk daarvan moet iemand iets doen: afhandelen of bewust uitsluiten. Er worden er drie afgehandeld. De andere vijf zijn niet afgehandeld en niet uitgesloten — ze zijn ",[277,581,582],{},"nooit overwogen."," Dat onderscheid telt: een uitgesloten geval is een besluit, een niet overwogen geval is een gat.",[175,585,586],{},"En dat aantal groeit niet met optellen maar met vermenigvuldigen.",[282,588,589,602],{},[285,590,591],{},[288,592,593,596,599],{},[291,594,595],{},"Velden die altijd samen voorkomen",[291,597,598],{},"Combinaties",[291,600,601],{},"Zinvol",[310,603,604,614,625,635],{},[288,605,606,609,612],{},[315,607,608],{},"3",[315,610,611],{},"8",[315,613,608],{},[288,615,616,619,622],{},[315,617,618],{},"4",[315,620,621],{},"16",[315,623,624],{},"3–4",[288,626,627,630,633],{},[315,628,629],{},"5",[315,631,632],{},"32",[315,634,624],{},[288,636,637,640,643],{},[315,638,639],{},"6",[315,641,642],{},"64",[315,644,624],{},[175,646,647,650],{},[277,648,649],{},"Drie: je betaalt bij elke leesbeurt, niet één keer per bug."," Dit is de belangrijkste. Een bug wordt één keer gefixt. Een afspraak die niet in het type staat, wordt opnieuw afgeleid door iedereen die dat scherm aanraakt — bij de review, bij het toevoegen van een feature, tijdens het uitzoeken van een incident. Dat zijn geen eenmalige kosten, dat is een belasting.",[175,652,653,654,656],{},"Daar komt ook de gebruikelijke pleister vandaan. Mensen dichten deze gaten met condities: hier controleren we dat we niet laden, daar dat de fout niet leeg is. Elk van die ",[188,655,572],{},"s is een pleister op een gat dat je zelf hebt geslagen toen je drie onafhankelijke velden declareerde. Vijf gaten, meestal twee pleisters — die op de gaten waar al iemand in is gevallen.",[175,658,659,660,663],{},"En let op, terzijde: niets hiervan ",[277,661,662],{},"crasht."," Geen crashrapport, geen stacktrace, niets in je foutentracker. Dus het argument dat monitoring het wel opvangt, gaat niet op. Maar dat is een bijeffect, niet de kern.",[175,665,666,667,670],{},"Wat de afgelopen paar jaar veranderde, is het ",[277,668,669],{},"volume."," Deze klasse verscheen vroeger één keer per week, geschreven door een mens die minstens dertig seconden over de velden nadacht. Nu verschijnt hij in twaalf seconden en heeft niemand hem gelezen — de agent niet, jij niet. En een reviewer is een mens die naar drie velden kijkt, niet iemand die acht combinaties in zijn hoofd houdt.",[538,672,674],{"id":673},"wie-de-invariant-vasthoudt","Wie de invariant vasthoudt",[175,676,677,678],{},"De vraag is dus nooit óf je een invariant hebt. Die heb je altijd. De vraag is ",[277,679,680],{},"wie ervoor verantwoordelijk is.",[175,682,683,684,686],{},"Meestal een mens. Een commentaar boven de klasse. Een wikipagina die al twee jaar niemand meer heeft geopend. Een reviewafspraak die twee van de vijf mensen zich herinneren. In het beste geval een ",[188,685,467],{}," in de constructor.",[688,689,692],"info-box",{":leading-icon":690,":title":691},"lucide:shield-off","Een assert is in Dart geen invariant",[175,693,694,695,697],{},"Dart verwijdert ",[188,696,467],{},"-statements uit releasebuilds. Niet uitgeschakeld, maar verwijderd: de conditie wordt nooit geëvalueerd en de argumenten evenmin, en daarom kost een dure controle binnen een assert in productie niets. Het praktische gevolg is precies het deel dat mensen vergeten: een invariant die je met een assert verdedigde, bestaat niet voor je gebruikers. Hij gold tijdens de ontwikkeling, waar je toch al keek, en hij ontbreekt op de enige plek waar je niet kijkt. Een assert is een debughulpmiddel. Het is geen beperking.",[175,699,700,701,704],{},"Dat is allemaal een invariant die iemand ",[277,702,703],{},"vasthoudt"," — met de hand, met aandacht, uit het hoofd. Het alternatief is het zo inrichten dat er niets vast te houden valt, omdat je het breken ervan niet in het type kunt uitdrukken. Niet \"wij controleren dat dit niet kan gebeuren\", maar \"dit compileert niet\".",[175,706,707,708],{},"En daarom hield dat op een academisch onderscheid te zijn. Een invariant die een mens vasthoudt, werkt precies zolang code op menselijke snelheid wordt geschreven. Een agent heeft je commentaar niet gelezen, je wiki niet geopend en niet bij je codereview gezeten. ",[277,709,710],{},"Afspraken schalen niet mee met generatiesnelheid. Compilers wel.",[439,712,714],{"id":713},"algebraïsche-datatypes-in-één-keer","Algebraïsche datatypes in één keer",[175,716,717,718,721],{},"Eén voorbehoud vóór de theorie, om de helft van de tegenwerpingen af te vangen. Het voorbeeld hier is bewust het meest voor de hand liggende dat er is. Laden, fout, succes is de meest afgekloven illustratie van algebraïsche datatypes op internet; hij staat in elke tutorial. Het idee heeft een beroemde leus, ",[530,719,720],{},"make illegal states unrepresentable",", meestal toegeschreven aan de lezingen van Yaron Minsky over effectief ML, en die bestaat al ruim tien jaar. Hier wordt niets ontdekt. Het interessante is waar die klasse bovenaan dit artikel vandaan kwam, en wat je doet als zulke code bij vrachtladingen tegelijk binnenkomt.",[175,723,724],{},"Nu de definitie. Zonder categorietheorie.",[175,726,727,730,731,734,735,737,738],{},[277,728,729],{},"Een product is \"en\"."," Een gewone klasse met velden: een string ",[277,732,733],{},"en"," een getal ",[277,736,733],{}," een bool. Het aantal mogelijke waarden is het product van de aantallen. Daar kwamen die acht vandaan: dat is geen retoriek, dat is rekenen. Elk veld dat je toevoegt ",[277,739,740],{},"vermenigvuldigt.",[175,742,743,746,747,750,751,754],{},[277,744,745],{},"Een som is \"of\"."," De waarde is dit of dat, en een derde optie bestaat niet. De eenvoudigste som die iedereen al kent, is een ",[188,748,749],{},"enum",". Een sealed class is diezelfde som, alleen kan elk geval zijn eigen data dragen. Hier worden de aantallen ",[277,752,753],{},"opgeteld",": laden is één waarde, mislukt is zoveel als er fouten zijn, geladen is zoveel als er lijsten zijn. Er wordt niets gratis vermenigvuldigd.",[175,756,757,758],{},"Het hele idee in één zin: ",[277,759,760],{},"je kunt het type niet zo construeren dat het een state voorstelt die nooit geldig was.",[538,762,764],{"id":763},"waarom-niet-gewoon-een-enum","\"Waarom niet gewoon een enum?\"",[175,766,767,768,770],{},"De voor de hand liggende tegenwerping is dat je hier geen sealed classes voor nodig hebt: voeg een ",[188,769,475],{}," met drie waarden toe en doe niet zo moeilijk. Dat instinct is redelijk, en het werkt niet.",[182,772,774],{"className":184,"code":773,"language":18,"meta":186,"style":186},"enum Status { loading, error, success }\n\nclass ProductsState {\n  final Status status;\n  final List\u003CProduct>? items;\n  final Exception? error;\n}\n",[188,775,776,796,802,810,821,839,852],{"__ignoreMap":186},[191,777,778,780,783,786,789,791,793],{"class":193,"line":194},[191,779,749],{"class":197},[191,781,782],{"class":201}," Status",[191,784,785],{"class":205}," { loading",[191,787,788],{"class":234},",",[191,790,261],{"class":205},[191,792,788],{"class":234},[191,794,795],{"class":205}," success }\n",[191,797,798],{"class":193,"line":209},[191,799,801],{"emptyLinePlaceholder":800},true,"\n",[191,803,804,806,808],{"class":193,"line":238},[191,805,198],{"class":197},[191,807,202],{"class":201},[191,809,206],{"class":205},[191,811,812,814,816,819],{"class":193,"line":251},[191,813,213],{"class":212},[191,815,782],{"class":201},[191,817,818],{"class":205}," status",[191,820,235],{"class":234},[191,822,823,825,827,829,831,833,835,837],{"class":193,"line":266},[191,824,213],{"class":212},[191,826,216],{"class":201},[191,828,219],{"class":205},[191,830,222],{"class":201},[191,832,225],{"class":205},[191,834,228],{"class":197},[191,836,231],{"class":205},[191,838,235],{"class":234},[191,840,842,844,846,848,850],{"class":193,"line":841},6,[191,843,213],{"class":212},[191,845,256],{"class":201},[191,847,228],{"class":197},[191,849,261],{"class":205},[191,851,235],{"class":234},[191,853,855],{"class":193,"line":854},7,[191,856,269],{"class":205},[175,858,859,860,863,864,867],{},"Tel opnieuw: drie statuswaarden, maal twee lijsttoestanden, maal twee fouttoestanden. ",[277,861,862],{},"Twaalf combinaties."," Het waren er acht. Nog steeds drie die iets betekenen. Rekenkundig is het dus ",[277,865,866],{},"slechter"," geworden: negen zinloze combinaties in plaats van vijf.",[175,869,870,871,874,875,566,878,874,880,882,883,886],{},"En het oorspronkelijke probleem staat er nog steeds. ",[188,872,873],{},"status"," is ",[188,876,877],{},"success",[188,879,300],{},[188,881,324],{},": compileert prima. In de UI schrijf je nog steeds ",[188,884,885],{},"items!",", waarmee je de compiler bezweert dat de data er echt is. Een belofte is geen garantie. Het is een uitgezette controle met prettige syntaxis.",[175,888,889,890,892,893,896,897,900],{},"Het verschil zit in wat de som draagt. Een ",[188,891,749],{}," is een som ",[277,894,895],{},"zonder data",": hij vertelt je in welke state je zit, maar de data ligt ernaast, aan de state gekoppeld door alleen jouw discipline. Een sealed class is een som ",[277,898,899],{},"met data",": de lijst bestaat precies daar waar hij iets betekent, en nergens anders.",[175,902,903,904,906,907,909,910],{},"De regel is dus: dragen je states geen data, gebruik dan een ",[188,905,749],{},", die is korter. Zodra ook maar één state een lading heeft, houdt de ",[188,908,749],{}," op te helpen, en ",[277,911,912,913,915],{},"elke ",[188,914,486],{}," in je UI is de rekening.",[175,917,918,919,922],{},"Het is een oud idee. ML, waar het vandaan komt, is vijftig jaar oud; Haskell, OCaml en F# hebben het al decennia. In de mainstream zit het nu vrijwel overal — Dart kreeg het in versie 3, in 2023. Wat veranderde, is niet het idee maar het argument ervoor. Algebraïsche datatypes werden verkocht op elegantie, en esthetische argumenten verliezen het van sprints. Het argument nu is dat ",[277,920,921],{},"code sneller geschreven wordt dan gelezen",", dat de enige reviewer die de generatie bijhoudt de compiler is, en dat alles wat je niet in het type uitdrukt, je uitdrukt als hoop.",[439,924,926],{"id":925},"de-herschrijving","De herschrijving",[175,928,929],{},"Drie stappen, ongeveer twee minuten werk.",[175,931,932,935],{},[277,933,934],{},"Stap één: schrijf de states in woorden op, vóór er code is."," Het scherm laadt. Het scherm is mislukt. Het scherm heeft een lijst. Dat is het — drie. Kom je bij deze stap op zeven uit, dan schrijf je vrijwel zeker vlagcombinaties op in plaats van states. Een state is iets wat je met één woord kunt benoemen en op een mockup kunt aanwijzen.",[175,937,938],{},[277,939,940],{},"Stap twee: elke state wordt zijn eigen klasse.",[182,942,944],{"className":184,"code":943,"language":18,"meta":186,"style":186},"sealed class ProductsState {\n  const ProductsState();\n}\n\nfinal class Loading extends ProductsState {\n  const Loading();\n}\n\nfinal class Failed extends ProductsState {\n  const Failed(this.error);\n  final Exception error;\n}\n\nfinal class Loaded extends ProductsState {\n  const Loaded(this.items);\n  final List\u003CProduct> items;\n}\n",[188,945,946,958,970,974,978,995,1005,1009,1014,1030,1051,1062,1067,1072,1088,1106,1122],{"__ignoreMap":186},[191,947,948,951,954,956],{"class":193,"line":194},[191,949,950],{"class":197},"sealed",[191,952,953],{"class":197}," class",[191,955,202],{"class":201},[191,957,206],{"class":205},[191,959,960,963,965,968],{"class":193,"line":209},[191,961,962],{"class":212},"  const",[191,964,202],{"class":201},[191,966,967],{"class":205},"()",[191,969,235],{"class":234},[191,971,972],{"class":193,"line":238},[191,973,269],{"class":205},[191,975,976],{"class":193,"line":251},[191,977,801],{"emptyLinePlaceholder":800},[191,979,980,983,985,988,991,993],{"class":193,"line":266},[191,981,982],{"class":212},"final",[191,984,953],{"class":197},[191,986,987],{"class":201}," Loading",[191,989,990],{"class":197}," extends",[191,992,202],{"class":201},[191,994,206],{"class":205},[191,996,997,999,1001,1003],{"class":193,"line":841},[191,998,962],{"class":212},[191,1000,987],{"class":201},[191,1002,967],{"class":205},[191,1004,235],{"class":234},[191,1006,1007],{"class":193,"line":854},[191,1008,269],{"class":205},[191,1010,1012],{"class":193,"line":1011},8,[191,1013,801],{"emptyLinePlaceholder":800},[191,1015,1017,1019,1021,1024,1026,1028],{"class":193,"line":1016},9,[191,1018,982],{"class":212},[191,1020,953],{"class":197},[191,1022,1023],{"class":201}," Failed",[191,1025,990],{"class":197},[191,1027,202],{"class":201},[191,1029,206],{"class":205},[191,1031,1033,1035,1037,1040,1044,1046,1049],{"class":193,"line":1032},10,[191,1034,962],{"class":212},[191,1036,1023],{"class":201},[191,1038,1039],{"class":205},"(",[191,1041,1043],{"class":1042},"s_hVV","this",[191,1045,487],{"class":234},[191,1047,1048],{"class":205},"error)",[191,1050,235],{"class":234},[191,1052,1054,1056,1058,1060],{"class":193,"line":1053},11,[191,1055,213],{"class":212},[191,1057,256],{"class":201},[191,1059,261],{"class":205},[191,1061,235],{"class":234},[191,1063,1065],{"class":193,"line":1064},12,[191,1066,269],{"class":205},[191,1068,1070],{"class":193,"line":1069},13,[191,1071,801],{"emptyLinePlaceholder":800},[191,1073,1075,1077,1079,1082,1084,1086],{"class":193,"line":1074},14,[191,1076,982],{"class":212},[191,1078,953],{"class":197},[191,1080,1081],{"class":201}," Loaded",[191,1083,990],{"class":197},[191,1085,202],{"class":201},[191,1087,206],{"class":205},[191,1089,1091,1093,1095,1097,1099,1101,1104],{"class":193,"line":1090},15,[191,1092,962],{"class":212},[191,1094,1081],{"class":201},[191,1096,1039],{"class":205},[191,1098,1043],{"class":1042},[191,1100,487],{"class":234},[191,1102,1103],{"class":205},"items)",[191,1105,235],{"class":234},[191,1107,1109,1111,1113,1115,1117,1120],{"class":193,"line":1108},16,[191,1110,213],{"class":212},[191,1112,216],{"class":201},[191,1114,219],{"class":205},[191,1116,222],{"class":201},[191,1118,1119],{"class":205},"> items",[191,1121,235],{"class":234},[191,1123,1125],{"class":193,"line":1124},17,[191,1126,269],{"class":205},[175,1128,1129,1130,1133,1134,1137,1138,1141,1142,1145,1146,1149],{},"Kijk wat er gebeurde. ",[188,1131,1132],{},"Failed"," draagt een fout, en die is ",[277,1135,1136],{},"niet nullable"," — de state \"we zijn mislukt maar er is geen fout\" bestaat niet meer, en je kunt hem fysiek niet construeren. ",[188,1139,1140],{},"Loaded"," draagt een lijst, ook niet nullable. ",[188,1143,1144],{},"Loading"," draagt ",[277,1147,1148],{},"niets",", want tijdens het laden is er geen data, en een veld ervoor openhouden is precies de uitnodiging tot een onmogelijke state.",[175,1151,1152,1153,1156],{},"Let op wat er niet staat. ",[277,1154,1155],{},"Geen enkele boolean. Geen enkel nullable veld."," Dat is de hele refactor; de rest is syntaxis.",[175,1158,1159,1160,1162,1163,1166,1167,1170],{},"Eén Dart-specifiek detail: ",[188,1161,950],{}," beperkt afgeleide types tot ",[277,1164,1165],{},"dezelfde library",", en dat is in de praktijk hetzelfde bestand, tenzij je het bewust opsplitst met ",[188,1168,1169],{},"part",". Dat is geen muggenzifterij maar het mechanisme: om volledigheid te controleren heeft de compiler de complete lijst met gevallen nodig, en dat kan hij alleen binnen de library garanderen. Vandaar de afspraak: hoeveel gevallen je ook hebt, ze wonen samen in één bestand. De eerste keer ziet het er vreemd uit en daarna blijkt het handig: de hele stateruimte van het scherm is in één oogopslag zichtbaar.",[175,1172,1173],{},[277,1174,1175],{},"Stap drie: de UI.",[182,1177,1179],{"className":184,"code":1178,"language":18,"meta":186,"style":186},"Widget build(BuildContext context) {\n  return switch (state) {\n    Loading() => const AppSpinner(),\n    Failed(:final error) => ErrorView(error),\n    Loaded(:final items) => ItemList(items),\n  };\n}\n",[188,1180,1181,1198,1210,1232,1257,1281,1288],{"__ignoreMap":186},[191,1182,1183,1186,1190,1192,1195],{"class":193,"line":194},[191,1184,1185],{"class":201},"Widget",[191,1187,1189],{"class":1188},"sGLFI"," build",[191,1191,1039],{"class":205},[191,1193,1194],{"class":201},"BuildContext",[191,1196,1197],{"class":205}," context) {\n",[191,1199,1200,1204,1207],{"class":193,"line":209},[191,1201,1203],{"class":1202},"sVHd0","  return",[191,1205,1206],{"class":1202}," switch",[191,1208,1209],{"class":205}," (state) {\n",[191,1211,1212,1215,1218,1221,1224,1227,1229],{"class":193,"line":238},[191,1213,1214],{"class":201},"    Loading",[191,1216,1217],{"class":205},"() ",[191,1219,1220],{"class":197},"=>",[191,1222,1223],{"class":212}," const",[191,1225,1226],{"class":201}," AppSpinner",[191,1228,967],{"class":205},[191,1230,1231],{"class":234},",\n",[191,1233,1234,1237,1239,1242,1244,1247,1249,1252,1255],{"class":193,"line":251},[191,1235,1236],{"class":201},"    Failed",[191,1238,1039],{"class":205},[191,1240,1241],{"class":197},":",[191,1243,982],{"class":212},[191,1245,1246],{"class":205}," error) ",[191,1248,1220],{"class":197},[191,1250,1251],{"class":201}," ErrorView",[191,1253,1254],{"class":205},"(error)",[191,1256,1231],{"class":234},[191,1258,1259,1262,1264,1266,1268,1271,1273,1276,1279],{"class":193,"line":266},[191,1260,1261],{"class":201},"    Loaded",[191,1263,1039],{"class":205},[191,1265,1241],{"class":197},[191,1267,982],{"class":212},[191,1269,1270],{"class":205}," items) ",[191,1272,1220],{"class":197},[191,1274,1275],{"class":201}," ItemList",[191,1277,1278],{"class":205},"(items)",[191,1280,1231],{"class":234},[191,1282,1283,1286],{"class":193,"line":841},[191,1284,1285],{"class":205},"  }",[191,1287,235],{"class":234},[191,1289,1290],{"class":193,"line":854},[191,1291,269],{"class":205},[175,1293,1294,1295,1297,1298,1301,1302,1304,1305,1308],{},"Geen enkele ",[188,1296,572],{},". Geen enkele null-check. De data wordt in het patroon gedestructureerd — ",[188,1299,1300],{},":final items"," — en komt al getypeerd binnen. Binnen de ",[188,1303,1140],{},"-tak ",[277,1306,1307],{},"bestaat"," de lijst, in plaats van dat hij zou kunnen bestaan. Vergelijk dat met wat je met vlaggen zou hebben: een keten van controles waarvan de volgorde ertoe doet en waarvan niemand de reden nog weet.",[538,1310,1312],{"id":1311},"hoe-je-dit-doet-in-een-project-met-veertig-schermen","Hoe je dit doet in een project met veertig schermen",[175,1314,1315,1316,1319],{},"Herschrijf niet alles. De regel: ",[277,1317,1318],{},"nieuw scherm, meteen sealed; oud scherm, als je er toch al iets aan het repareren bent."," Een scherm dat niemand in twee jaar heeft aangeraakt, laat je met rust. Er zitten waarschijnlijk onmogelijke states in, maar die zijn ofwel al afgegaan, ofwel gaan ze dat nooit doen.",[175,1321,1322,1323,1326,1327,1330,1331,1336],{},"Voor de schermen die je wél aanraakt, komt de prioriteit neer op één ding: ",[277,1324,1325],{},"op hoeveel plekken die state gelezen wordt."," Eén ",[188,1328,1329],{},"switch"," in één widget: herschrijven loont nauwelijks, want je ziet toch alles. Een state die op zes plekken gelezen wordt — UI, analytics, logging en een pushafhandelaar — is waar volledigheid zichzelf terugverdient, want dat zijn precies de vijf van de zes die vergeten worden. Loopt de state van een scherm al door één enkele stream in plaats van door ",[1332,1333,1335],"a",{"href":1334},"\u002Fblog\u002Fai-agents-struggle-with-flutter","een piramide van vlaggen",", dan is dat de goedkoopste plek om te beginnen.",[439,1338,1340],{"id":1339},"hetzelfde-type-in-vier-andere-talen","Hetzelfde type in vier andere talen",[175,1342,1343],{},"Niet omdat je een tweede taal nodig hebt, maar zodat duidelijk is dat dit geen Dart-dialect of een mode uit de Flutter-gemeenschap is. Hetzelfde type, drie states, data aan de state vast.",[175,1345,1346],{},[277,1347,1348],{},"Kotlin.",[182,1350,1353],{"className":1351,"code":1352,"language":71,"meta":186,"style":186},"language-kotlin shiki shiki-themes material-theme-lighter github-light github-dark","sealed interface ProductsState\n\ndata object Loading : ProductsState\n\ndata class Failed(\n    val error: AppError,\n) : ProductsState\n\ndata class Loaded(\n    val items: List\u003CProduct>,\n) : ProductsState\n",[188,1354,1355,1366,1370,1386,1390,1401,1414,1421,1425,1435,1452],{"__ignoreMap":186},[191,1356,1357,1359,1362],{"class":193,"line":194},[191,1358,950],{"class":212},[191,1360,1361],{"class":197}," interface",[191,1363,1365],{"class":1364},"sbgvK"," ProductsState\n",[191,1367,1368],{"class":193,"line":209},[191,1369,801],{"emptyLinePlaceholder":800},[191,1371,1372,1375,1378,1380,1383],{"class":193,"line":238},[191,1373,1374],{"class":212},"data",[191,1376,1377],{"class":197}," object",[191,1379,987],{"class":1364},[191,1381,1382],{"class":205}," : ",[191,1384,1385],{"class":1364},"ProductsState\n",[191,1387,1388],{"class":193,"line":251},[191,1389,801],{"emptyLinePlaceholder":800},[191,1391,1392,1394,1396,1398],{"class":193,"line":266},[191,1393,1374],{"class":212},[191,1395,953],{"class":197},[191,1397,1023],{"class":1364},[191,1399,1400],{"class":205},"(\n",[191,1402,1403,1406,1409,1412],{"class":193,"line":841},[191,1404,1405],{"class":197},"    val",[191,1407,1408],{"class":205}," error: ",[191,1410,1411],{"class":1364},"AppError",[191,1413,1231],{"class":205},[191,1415,1416,1419],{"class":193,"line":854},[191,1417,1418],{"class":205},") : ",[191,1420,1385],{"class":1364},[191,1422,1423],{"class":193,"line":1011},[191,1424,801],{"emptyLinePlaceholder":800},[191,1426,1427,1429,1431,1433],{"class":193,"line":1016},[191,1428,1374],{"class":212},[191,1430,953],{"class":197},[191,1432,1081],{"class":1364},[191,1434,1400],{"class":205},[191,1436,1437,1439,1442,1445,1447,1449],{"class":193,"line":1032},[191,1438,1405],{"class":197},[191,1440,1441],{"class":205}," items: ",[191,1443,1444],{"class":1364},"List",[191,1446,219],{"class":205},[191,1448,222],{"class":1364},[191,1450,1451],{"class":205},">,\n",[191,1453,1454,1456],{"class":193,"line":1053},[191,1455,1418],{"class":205},[191,1457,1385],{"class":1364},[175,1459,1460,1461,1464,1465,487],{},"Wordt ",[188,1462,1463],{},"when"," als expressie gebruikt, dan moet het elke tak dekken — precies onze ",[188,1466,1329],{},[175,1468,1469,1472,1473,1241],{},[277,1470,1471],{},"Rust."," Interessanter, want dit is de ingebouwde ",[188,1474,749],{},[182,1476,1479],{"className":1477,"code":1478,"language":126,"meta":186,"style":186},"language-rust shiki shiki-themes material-theme-lighter github-light github-dark","enum ProductsState {\n    Loading,\n    Failed(AppError),\n    Loaded(Vec\u003CProduct>),\n}\n",[188,1480,1481,1489,1495,1506,1522],{"__ignoreMap":186},[191,1482,1483,1485,1487],{"class":193,"line":194},[191,1484,749],{"class":212},[191,1486,202],{"class":1364},[191,1488,206],{"class":234},[191,1490,1491,1493],{"class":193,"line":209},[191,1492,1214],{"class":1364},[191,1494,1231],{"class":234},[191,1496,1497,1499,1501,1503],{"class":193,"line":238},[191,1498,1236],{"class":1188},[191,1500,1039],{"class":234},[191,1502,1411],{"class":1364},[191,1504,1505],{"class":234},"),\n",[191,1507,1508,1510,1512,1515,1517,1519],{"class":193,"line":251},[191,1509,1261],{"class":1188},[191,1511,1039],{"class":234},[191,1513,1514],{"class":1364},"Vec",[191,1516,219],{"class":234},[191,1518,222],{"class":1364},[191,1520,1521],{"class":234},">),\n",[191,1523,1524],{"class":193,"line":266},[191,1525,269],{"class":234},[175,1527,1528,1529,1532,1533,1535,1536,1539],{},"In Rust ",[277,1530,1531],{},"is"," een ",[188,1534,749],{}," vanaf het begin een som met data, zonder extra ceremonie. Mis je een geval in een ",[188,1537,1538],{},"match",", dan compileert het niet.",[175,1541,1542,1544],{},[277,1543,139],{},", hetzelfde met associated values:",[182,1546,1549],{"className":1547,"code":1548,"language":141,"meta":186,"style":186},"language-swift shiki shiki-themes material-theme-lighter github-light github-dark","enum ProductsState {\n    case loading\n    case failed(AppError)\n    case loaded([Product])\n}\n",[188,1550,1551,1559,1567,1581,1596],{"__ignoreMap":186},[191,1552,1553,1555,1557],{"class":193,"line":194},[191,1554,749],{"class":212},[191,1556,202],{"class":1364},[191,1558,206],{"class":234},[191,1560,1561,1564],{"class":193,"line":209},[191,1562,1563],{"class":212},"    case",[191,1565,1566],{"class":1042}," loading\n",[191,1568,1569,1571,1574,1576,1578],{"class":193,"line":238},[191,1570,1563],{"class":212},[191,1572,1573],{"class":1042}," failed",[191,1575,1039],{"class":234},[191,1577,1411],{"class":205},[191,1579,1580],{"class":234},")\n",[191,1582,1583,1585,1588,1591,1593],{"class":193,"line":251},[191,1584,1563],{"class":212},[191,1586,1587],{"class":1042}," loaded",[191,1589,1590],{"class":234},"([",[191,1592,222],{"class":205},[191,1594,1595],{"class":234},"])\n",[191,1597,1598],{"class":193,"line":266},[191,1599,269],{"class":234},[175,1601,1602,1605],{},[277,1603,1604],{},"En de exoot: Idris"," — de ML-syntaxis waar al het bovenstaande van afstamt:",[182,1607,1611],{"className":1608,"code":1609,"language":1610,"meta":186,"style":186},"language-idris shiki shiki-themes material-theme-lighter github-light github-dark","data ProductsState\n  = Loading\n  | Failed AppError\n  | Loaded (List Product)\n","idris",[188,1612,1613,1618,1623,1628],{"__ignoreMap":186},[191,1614,1615],{"class":193,"line":194},[191,1616,1617],{},"data ProductsState\n",[191,1619,1620],{"class":193,"line":209},[191,1621,1622],{},"  = Loading\n",[191,1624,1625],{"class":193,"line":238},[191,1626,1627],{},"  | Failed AppError\n",[191,1629,1630],{"class":193,"line":251},[191,1631,1632],{},"  | Loaded (List Product)\n",[175,1634,1635,1636,1639],{},"Kijk naar de verticale streep. Die leest als \"of\". ",[277,1637,1638],{},"Het somtype wordt letterlijk met een of-symbool geschreven",", en daar komt de naam vandaan: dit is een som lang voordat iemand het een sealed class noemde.",[175,1641,1642],{},"Idris doet daarnaast iets wat geen van de andere vier kan:",[182,1644,1646],{"className":1608,"code":1645,"language":1610,"meta":186,"style":186},"import Data.Vect\n\ndata ProductsState : Type where\n  Loading : ProductsState\n  Failed  : AppError -> ProductsState\n  Loaded  : Vect (S n) Product -> ProductsState\n",[188,1647,1648,1653,1657,1662,1667,1672],{"__ignoreMap":186},[191,1649,1650],{"class":193,"line":194},[191,1651,1652],{},"import Data.Vect\n",[191,1654,1655],{"class":193,"line":209},[191,1656,801],{"emptyLinePlaceholder":800},[191,1658,1659],{"class":193,"line":238},[191,1660,1661],{},"data ProductsState : Type where\n",[191,1663,1664],{"class":193,"line":251},[191,1665,1666],{},"  Loading : ProductsState\n",[191,1668,1669],{"class":193,"line":266},[191,1670,1671],{},"  Failed  : AppError -> ProductsState\n",[191,1673,1674],{"class":193,"line":841},[191,1675,1676],{},"  Loaded  : Vect (S n) Product -> ProductsState\n",[175,1678,1679,1682,1683,1686,1687,1689],{},[188,1680,1681],{},"Vect (S n)"," in het type betekent een lijst die ",[277,1684,1685],{},"gegarandeerd minstens één element heeft."," Niet \"we hebben het gecontroleerd\", niet \"we hebben het afgesproken\" — je kunt ",[188,1688,1140],{}," eenvoudigweg niet met een lege lijst construeren. Onthoud dit fragment, het komt aan het eind terug.",[175,1691,1692],{},"Eén idee, vijf talen, overal volledigheidscontrole. Werk je niet met Dart, dan werkt alles hieronder net zo goed voor jou.",[439,1694,1696],{"id":1695},"volledigheid-waar-dit-allemaal-voor-was","Volledigheid, waar dit allemaal voor was",[175,1698,1699],{},"Een maand later komt er een eis binnen: als de lijst leeg terugkomt, toon dan een placeholder met een knop. Dat is geen fout en geen succes-met-data. Het is een vierde state.",[182,1701,1703],{"className":184,"code":1702,"language":18,"meta":186,"style":186},"final class Empty extends ProductsState {\n  const Empty();\n}\n",[188,1704,1705,1720,1730],{"__ignoreMap":186},[191,1706,1707,1709,1711,1714,1716,1718],{"class":193,"line":194},[191,1708,982],{"class":212},[191,1710,953],{"class":197},[191,1712,1713],{"class":201}," Empty",[191,1715,990],{"class":197},[191,1717,202],{"class":201},[191,1719,206],{"class":205},[191,1721,1722,1724,1726,1728],{"class":193,"line":209},[191,1723,962],{"class":212},[191,1725,1713],{"class":201},[191,1727,967],{"class":205},[191,1729,235],{"class":234},[191,1731,1732],{"class":193,"line":238},[191,1733,269],{"class":205},[175,1735,1736],{},"Eén regel. Er is verder niets veranderd. En het project compileert niet:",[182,1738,1743],{"className":1739,"code":1741,"language":1742},[1740],"language-text","The type 'ProductsState' is not exhaustively matched by the switch\ncases since it doesn't match 'Empty()'.\n","text",[188,1744,1741],{"__ignoreMap":186},[175,1746,1747,1748,1751],{},"De compiler somt ",[277,1749,1750],{},"elke"," plek op waar de nieuwe state niet wordt afgehandeld. Niet één ervan — allemaal.",[175,1753,1754,1755,1757,1758,1761],{},"Even waarom dit in een artikel over door AI geschreven code thuishoort: \"Voeg een lege state toe aan het lijstscherm\" is precies het soort taak dat mensen zonder te kijken aan een agent geven. De agent voegt de klasse toe, werkt de ",[188,1756,1329],{}," bij die hij in zijn context had, en werkt ",[277,1759,1760],{},"de andere twee niet bij"," — die in de analytics en die achter de vernieuwknop. Ze stonden niet in de prompt, dus wat hem betreft bestaan ze niet.",[175,1763,1764],{},"Met vlaggen gaat dat stilzwijgend live. Er is geen diff die het opvangt, want de diff bevat alleen wat de agent veranderde. De tests zijn groen, want een state die een maand geleden niet bestond heeft per definitie geen tests. Je hoort het van een gebruiker, zes weken later, verwoord als \"soms krijg ik gewoon een leeg scherm\".",[175,1766,1767],{},"Met sealed classes gaat het nergens heen. Het compileert gewoon niet.",[175,1769,1770,1771,1774],{},"En dit is het deel dat het meenemen waard is. ",[277,1772,1773],{},"Volledigheid is geen bescherming tegen AI. Het is feedback ervoor."," Waar een agent slecht in is, is dit: hij heeft geen toegang tot het lijstje \"en vergeet dit stukje hier ook niet\" in jouw hoofd. De compiler geeft dat lijstje machineleesbaar af, met bestanden en regelnummers. De agent loopt tegen rood aan en repareert alle drie, en dat kan hij oprecht goed, want hem is precies verteld waar. Je houdt hem niet tegen — je geeft hem wat hij miste. Hetzelfde mechanisme helpt een mens één keer per week en een agent elke twintig minuten.",[538,1776,1778],{"id":1777},"de-ene-regel-die-het-allemaal-uitzet","De ene regel die het allemaal uitzet",[182,1780,1782],{"className":184,"code":1781,"language":18,"meta":186,"style":186},"return switch (state) {\n  Loading() => const AppSpinner(),\n  _ => ItemList(state.items),\n};\n",[188,1783,1784,1793,1810,1828],{"__ignoreMap":186},[191,1785,1786,1789,1791],{"class":193,"line":194},[191,1787,1788],{"class":1202},"return",[191,1790,1206],{"class":1202},[191,1792,1209],{"class":205},[191,1794,1795,1798,1800,1802,1804,1806,1808],{"class":193,"line":209},[191,1796,1797],{"class":201},"  Loading",[191,1799,1217],{"class":205},[191,1801,1220],{"class":197},[191,1803,1223],{"class":212},[191,1805,1226],{"class":201},[191,1807,967],{"class":205},[191,1809,1231],{"class":234},[191,1811,1812,1815,1817,1819,1822,1824,1826],{"class":193,"line":238},[191,1813,1814],{"class":205},"  _ ",[191,1816,1220],{"class":197},[191,1818,1275],{"class":201},[191,1820,1821],{"class":205},"(state",[191,1823,487],{"class":234},[191,1825,1103],{"class":205},[191,1827,1231],{"class":234},[191,1829,1830,1833],{"class":193,"line":251},[191,1831,1832],{"class":205},"}",[191,1834,235],{"class":234},[175,1836,1837],{},"Eén underscore en de volledigheidscontrole is dood. Stilzwijgend, zonder waarschuwing. Een vangnettak handelt per definitie alles af, dus er blijft niets voor de compiler over om te controleren. Voeg een vijfde state toe, een zesde, een tiende — het compileert, en valt hier stilzwijgend doorheen.",[175,1839,1840],{},"Neem je precies één reviewregel mee uit dit artikel, neem dan deze:",[1842,1843,1844],"blockquote",{},[175,1845,1846],{},[277,1847,1848],{},"Een switch over een sealed type hoort geen default-tak te hebben.",[175,1850,1851],{},"Het is de enige regel hier die je met het oog zóú moeten controleren — en een lintregel vangt hem, zodat je dat niet eens hoeft te doen.",[439,1853,1855],{"id":1854},"waar-algebraïsche-datatypes-niet-helpen","Waar algebraïsche datatypes niet helpen",[175,1857,1858],{},"Vier plekken waar ze niet werken of tegen je werken. Zonder deze sectie is de rest een preek.",[175,1860,1861,1864,1865,1867,1868,1871],{},[277,1862,1863],{},"Een som repareert geen product."," Je hebt het scherm in drie states gesplitst, en dat is goed. Maar ",[188,1866,1140],{}," is nog steeds een gewone klasse met velden, en zijn dat er twaalf waarvan de helft nullable, dan heb je het moeras alleen een verdieping lager gelegd. Algebraïsche datatypes gaan over ",[277,1869,1870],{},"welke states bestaan",", niet over wat er binnen een state zit.",[175,1873,1874,1877,1878,1880,1881,1883,1884,1887,1888,1890,1891,1893,1894,566,1897,1900],{},[277,1875,1876],{},"States die elkaar overlappen — de meest gemaakte fout bij de overstap."," Er komt een eis binnen voor pull-to-refresh: de lijst staat al op het scherm en je haalt een verse op. Is dat ",[188,1879,1144],{}," of ",[188,1882,1140],{},"? Het naïeve antwoord is een ",[188,1885,1886],{},"Refreshing","-geval met ",[188,1889,300],{}," erin. Een week later heb je ",[188,1892,1886],{},", ",[188,1895,1896],{},"RefreshingAfterError",[188,1898,1899],{},"FailedButHasCache"," — dezelfde combinatorische explosie, nu in klassen, en dat is merkbaar erger dan vlaggen omdat het omslachtiger is.",[175,1902,1903],{},"Het juiste antwoord bevalt mensen niet:",[182,1905,1907],{"className":184,"code":1906,"language":18,"meta":186,"style":186},"final class Loaded extends ProductsState {\n  const Loaded(this.items, {this.isRefreshing = false});\n  final List\u003CProduct> items;\n  final bool isRefreshing;\n}\n",[188,1908,1909,1923,1961,1975,1986],{"__ignoreMap":186},[191,1910,1911,1913,1915,1917,1919,1921],{"class":193,"line":194},[191,1912,982],{"class":212},[191,1914,953],{"class":197},[191,1916,1081],{"class":201},[191,1918,990],{"class":197},[191,1920,202],{"class":201},[191,1922,206],{"class":205},[191,1924,1925,1927,1929,1931,1933,1935,1937,1939,1942,1944,1946,1949,1952,1956,1959],{"class":193,"line":209},[191,1926,962],{"class":212},[191,1928,1081],{"class":201},[191,1930,1039],{"class":205},[191,1932,1043],{"class":1042},[191,1934,487],{"class":234},[191,1936,300],{"class":205},[191,1938,788],{"class":234},[191,1940,1941],{"class":205}," {",[191,1943,1043],{"class":1042},[191,1945,487],{"class":234},[191,1947,1948],{"class":205},"isRefreshing ",[191,1950,1951],{"class":197},"=",[191,1953,1955],{"class":1954},"s39Yj"," false",[191,1957,1958],{"class":205},"})",[191,1960,235],{"class":234},[191,1962,1963,1965,1967,1969,1971,1973],{"class":193,"line":238},[191,1964,213],{"class":212},[191,1966,216],{"class":201},[191,1968,219],{"class":205},[191,1970,222],{"class":201},[191,1972,1119],{"class":205},[191,1974,235],{"class":234},[191,1976,1977,1979,1981,1984],{"class":193,"line":251},[191,1978,213],{"class":212},[191,1980,243],{"class":201},[191,1982,1983],{"class":205}," isRefreshing",[191,1985,235],{"class":234},[191,1987,1988],{"class":193,"line":266},[191,1989,269],{"class":205},[175,1991,1992,1993,1996],{},"Ja, de boolean is terug, en dat is ",[277,1994,1995],{},"prima."," Hij woont nu binnen een state waar hij iets betekent: \"we hebben een lijst en we zijn aan het verversen\" is een echte situatie die je moet kunnen uitdrukken. En \"verversen zonder lijst\" bestaat niet meer, want er is geen plek meer om het neer te zetten.",[688,1998,2001],{":leading-icon":1999,":title":2000},"lucide:git-merge","Sommen voor wat elkaar uitsluit, producten voor wat samen voorkomt",[175,2002,2003],{},"Dit is de regel die bepaalt of de refactor helpt of schaadt. Twee dingen die nooit tegelijk waar kunnen zijn, vragen om een som: aparte gevallen van een sealed type. Twee dingen die routinematig tegelijk waar zijn, vragen om een product: velden naast elkaar binnen één geval. Die twee omdraaien is de belangrijkste manier om slechter uit te komen dan waar je begon, want elkaar uitsluitende states gemodelleerd als parallelle booleans geven je het probleem van de acht combinaties, en samen voorkomende feiten gemodelleerd als aparte gevallen geven je een klasse per combinatie.",[175,2005,2006,2009,2010,2013,2014,2017,2018,2020],{},[277,2007,2008],{},"Types houden niet elk soort invariant vast, en dit telt zwaarder dan de rest."," Een sealed class drukt een ",[277,2011,2012],{},"structurele"," invariant prachtig uit: welke states bestaan en welke data met welke state meereist. Over ",[277,2015,2016],{},"waarde-invarianten"," zegt hij niets: deze lijst is gesorteerd, de startdatum ligt vóór de einddatum, de regels tellen op tot het totaal, deze string is een geldig e-mailadres. Dat alles wordt nog steeds door een mens vastgehouden, en hoeveel ",[188,2019,950],{}," je er ook tegenaan gooit, dat verandert niets.",[175,2022,2023,2024,2026],{},"Dat Idris-fragment is precies het tegenvoorbeeld. ",[188,2025,1681],{}," — niet leeg, in het type — is een waarde-invariant die het typesysteem in is geduwd. Dat heet dependent types, en je zou \"gesorteerd\" en \"start vóór eind\" op dezelfde manier kunnen uitdrukken. De prijs is een taal waarin je niet schrijft en, realistisch gezien, niet gaat schrijven. In de praktijk grijp je naar een andere techniek: een private constructor met validatie, zodat het type niet in een ongeldige vorm gemaakt kan worden.",[175,2028,2029,2030,2033,2034,2037,2038,2041,2042,2044,2045],{},"In dezelfde categorie zitten de ",[277,2031,2032],{},"systeemgrenzen."," JSON die binnenkomt is niet getypeerd. Algebraïsche datatypes beginnen ",[530,2035,2036],{},"na"," het parsen, en een sealed class redt je niet wanneer de backend ",[188,2039,2040],{},"succes"," stuurt waar je code ",[188,2043,877],{}," verwacht — expliciete deserialisatie met sleutels die je met de hand schrijft wel. De zin die het bewaren waard is: ",[277,2046,2047],{},"algebraïsche datatypes elimineren onmogelijke combinaties van states, niet onmogelijke waarden.",[175,2049,2050,2053,2054,2056,2057,2059,2060,2063,2064],{},[277,2051,2052],{},"En overengineering."," Een sealed class met twee gevallen zonder data is een ",[188,2055,749],{},", en de ",[188,2058,749],{}," leest sneller. Een sealed class met één geval is een klasse. Een eerlijke boolean blijft een eerlijke boolean: ",[188,2061,2062],{},"isSelected"," op een checkbox is precies twee states, allebei geldig, en een algebraïsch datatype verbetert daar niets. De toets is simpel: tel de combinaties en streep de zinloze weg. ",[277,2065,2066],{},"Valt er niets weg te strepen, laat het dan met rust.",[175,2068,2069,2070,2073],{},"Tot slot de eerlijke prijs: ",[277,2071,2072],{},"het is omslachtig."," Vier states in Dart is zo'n dertig regels tegenover vier velden. De helft van het bezwaar — dat het trager schrijft — overleeft 2026 niet: dat is precies het werk dat een agent in seconden en zonder fouten doet. De andere helft, dat het trager leest, geldt onverminderd, en dat is de prijs die je betaalt.",[439,2075,2077],{"id":2076},"vind-ze-in-je-eigen-code-in-een-half-uur","Vind ze in je eigen code, in een half uur",[2079,2080],"how-to-schema",{":description":2081,":name":2082,":steps":2083,":total-time":2084},"Een oefening van een half uur die een abstract argument over types verandert in een concreet aantal ongeldige states dat je code vandaag toelaat.","Hoe je onmogelijke states in je eigen codebase vindt","[{\"name\": \"Kies één stateklasse\", \"text\": \"Zoek een klasse met drie of meer velden die altijd samen voorkomen: booleans, nullables of een mix. Een schermstate, een formulierstate, een wrapper om een verzoek. Kies de klasse die op de meeste plekken gelezen wordt, niet degene die er het slechtst uitziet.\"}, {\"name\": \"Schrijf elke combinatie uit\", \"text\": \"Tel per veld de toestanden die ertoe doen: een bool heeft er twee, een nullable veld heeft er twee, een enum heeft er zoveel als hij waarden heeft. Vermenigvuldig ze. Drie velden zijn acht rijen, vier zijn er zestien. Schrijf de rijen op in plaats van erover te redeneren, want het hele punt is dat niemand zestien rijen in zijn hoofd houdt.\"}, {\"name\": \"Streep weg wat nooit gebeurt\", \"text\": \"Ga rij voor rij en markeer elke rij als een echte situatie waarin het product kan verkeren of als iets wat nooit gebeurt. Wees streng: moet je een verhaal verzinnen waarom een rij zou kunnen voorkomen, dan komt hij niet voor.\"}, {\"name\": \"Tel wat overblijft\", \"text\": \"Elke rij die je hebt weggestreept is een onmogelijke state die vandaag compileert. Vijf van de acht is de gebruikelijke uitkomst. Dat getal is de omvang van het gat tussen wat je type toelaat en wat je domein toestaat.\"}, {\"name\": \"Zoek naar de default-tak\", \"text\": \"Zoek in de codebase naar switch-statements over die state en kijk of er een default-tak of underscore in zit. Elke daarvan is een plek waar een nieuwe state stilzwijgend doorvalt in plaats van de build te laten falen.\"}, {\"name\": \"Herschrijf eerst de meest gelezen klasse\", \"text\": \"Zet die ene klasse om naar een sealed hiërarchie met niet-nullable velden en repareer elke compileerfout die dat oplevert. Die fouten zijn de lijst met plekken die leunden op een afspraak die niemand had opgeschreven.\"}]","PT30M",[2086,2087],"checklist",{":groups":2088,":subtitle":2089,":title":2090},"[{\"title\": \"Het type zelf\", \"items\": [\"Elkaar uitsluitende situaties zijn aparte gevallen van een sealed type, geen parallelle booleans\", \"Elk veld binnen een geval is niet-nullable, of de null betekent iets specifieks en dat is vastgelegd\", \"Data hoort bij de state die haar bezit, niet naast een status-enum\", \"Feiten die samen voorkomen zijn velden binnen één geval, geen geval per combinatie\"]}, {\"title\": \"Elke switch erover\", \"items\": [\"Geen default-tak en geen underscore-patroon over een sealed type\", \"Geen geforceerd uitpakken van een veld dat de state juist hoort te garanderen\", \"Elke lezer van de state is gedekt, inclusief analytics, logging en achtergrondafhandelaars\"]}, {\"title\": \"Wat types niet voor je doen\", \"items\": [\"Waarde-invarianten — gesorteerd, niet leeg, binnen bereik, welgevormd — worden in een constructor afgedwongen, niet aangenomen\", \"Parsen op de systeemgrens valideert expliciet in plaats van op de vorm van de data te vertrouwen\", \"Asserts gelden als debughulpmiddel, want in releasebuilds draaien ze niet\"]}]","Tien controles in drie groepen. Alles wat niet aangevinkt is, is een besluit dat nog niemand heeft genomen.","Een stateklasse reviewen",[439,2092,2094],{"id":2093},"de-korte-versie","De korte versie",[175,2096,2097],{},"Een onmogelijke state is er een die je type toelaat en je domein verbiedt, en dat is hetzelfde als een gebroken invariant. Duur is hij niet omdat er tijdens runtime iets stukgaat, maar omdat elke afspraak die niet in het type staat door iemand opnieuw wordt afgeleid bij elke leesbeurt.",[175,2099,2100],{},"De invariant bestaat altijd. De enige vraag is of een mens hem vasthoudt — met een commentaar, een wikipagina en een assert die in release niet draait — of dat er niets vast te houden valt omdat je hem niet kúnt breken: dat compileert niet.",[175,2102,2103],{},"Drie velden die altijd samen voorkomen, acht combinaties, drie daarvan zinvol. Een sealed class met niet-nullable velden houdt precies die drie over en maakt de andere vijf onbouwbaar. Volledigheid is de opbrengst: die verandert \"iemand vergat de afhandeling bij te werken\" in \"het project compileert niet\", en werkt hetzelfde ongeacht wie het vergat. En een default-tak zet dat allemaal uit, stilzwijgend.",[175,2105,2106],{},"En dan de grotere verschuiving. Types waren vroeger de manier waarop je je denkwerk uitlegde aan een collega die het bestand een half jaar later opende. Nu zijn ze de enige manier om het uit te leggen aan iets dat sneller code schrijft dan jij kunt lezen. Het vak is niet verdwenen — het is een niveau omhooggegaan. Vroeger schreef je de implementatie. Nu schrijf je de grenzen waarbinnen de implementatie moet blijven, en invullen kan iedereen. Een machine incluis.",[439,2108,2110],{"id":2109},"veelgestelde-vragen","Veelgestelde vragen",[2112,2113],"questions",{":items":2114},"[{\"title\": \"Wat is een onmogelijke state in programmeren?\", \"text\": \"Een state die je type toelaat en je domein verbiedt. Een schermstateklasse met een boolean voor laden, een nullable lijst en een nullable fout heeft acht combinaties, maar slechts drie daarvan beschrijven een situatie waarin het product werkelijk kan verkeren. De overige vijf compileren, komen door de review en gaan naar productie. Hetzelfde idee heet preciezer een gebroken invariant: de invariant is de regel die altijd moet gelden, en de onmogelijke state is wat je krijgt wanneer het type die niet afdwingt.\"}, {\"title\": \"Wat zijn algebraïsche datatypes?\", \"text\": \"Een manier om te beschrijven hoeveel waarden een type kan aannemen. Een producttype is een en-type: een gewone klasse met velden, waarvan het aantal waarden het product is van de aantallen per veld, zodat elk toegevoegd veld vermenigvuldigt. Een somtype is een of-type: de waarde hoort bij het ene geval of het andere en een derde optie bestaat niet, dus de aantallen worden opgeteld in plaats van vermenigvuldigd. Een enum is een som zonder data, een sealed class is een som waarin elk geval eigen data draagt. Het nut van sommen is dat je geen waarde kunt construeren die een state voorstelt die nooit geldig was.\"}, {\"title\": \"Waarom volstaat een enum met een statusveld niet?\", \"text\": \"Omdat het de rekensom verslechtert. Een enum met drie waarden naast een nullable lijst en een nullable fout geeft twaalf combinaties in plaats van acht, en nog steeds betekenen er maar drie iets. Het oorspronkelijke probleem blijft: de status kan success zijn terwijl de lijst null is, dat compileert, en de UI eindigt met het geforceerd uitpakken van het veld. Een enum vertelt je in welke state je zit maar laat de data ernaast liggen, alleen door afspraak aan de state gekoppeld. Een sealed class koppelt de data aan de state, zodat de lijst precies bestaat waar hij iets betekent.\"}, {\"title\": \"Moeten sealed classes in Dart in één bestand staan?\", \"text\": \"In dezelfde library, en dat is in de praktijk hetzelfde bestand tenzij je het bewust opsplitst met part. Dat is het mechanisme, geen stijlregel: om volledigheid te controleren heeft de compiler de complete lijst met subtypes nodig, en volledigheid kan hij alleen binnen de library garanderen. De afspraak die daaruit volgt is om alle gevallen van een state samen in één bestand te houden, en dat blijkt handig: de hele stateruimte van een scherm is in één oogopslag zichtbaar.\"}, {\"title\": \"Waarom breekt een default-tak de volledigheidscontrole?\", \"text\": \"Omdat een vangnettak per definitie elk geval afhandelt, zodat er voor de compiler niets te verifiëren overblijft. Zodra een switch over een sealed type een default-tak of underscore heeft, compileert het toevoegen van een vijfde of tiende state schoon en valt het stilzwijgend in die tak in plaats van de build te laten falen. Dit is de waardevolste reviewregel over het onderwerp: een switch over een sealed type hoort geen default-tak te hebben, en een lintregel kan dat afdwingen.\"}, {\"title\": \"Helpen sealed classes bij door AI gegenereerde code?\", \"text\": \"Ja, maar niet als barrière. Volledigheid is eerder feedback dan bescherming. Vraag je een agent om een nieuwe state toe te voegen, dan werkt hij de switch bij die hij in zijn context had en mist hij de switches die er niet in zaten, want die stonden niet in de prompt. Met vlaggen gaat dat stilzwijgend live, want de diff toont alleen wat er veranderde en een gloednieuwe state heeft geen tests. Met een sealed type faalt de build en noemt de compiler elke onafgehandelde plek met bestand en regelnummer, en dat is precies de invoer waar een agent goed mee omgaat.\"}, {\"title\": \"Waar helpen algebraïsche datatypes niet?\", \"text\": \"Op vier plekken. Ze repareren geen geval dat zelf een opgeblazen producttype met twaalf nullable velden is. Ze schaden wanneer ze gebruikt worden voor states die elkaar werkelijk overlappen, zoals verversen terwijl de data al op het scherm staat, wat om een boolean binnen het geladen geval vraagt in plaats van om een nieuw geval. Ze zeggen niets over waarde-invarianten zoals gesorteerd, niet leeg of een geldig e-mailadres, waarvoor nog steeds een validerende constructor nodig is. En ze beginnen na het parsen, dus op de systeemgrens — waar de backend een verkeerd gespelde statusstring stuurt — doen ze niets.\"}]",[175,2116,2117,2118,2121,2122,2126,2127,487],{},"Wil je de bredere lijst van wat agenten zonder toezicht met Flutter-code doen, dan is dit nummer drie in onze ",[1332,2119,2120],{"href":1334},"uitsplitsing van zeven punten",", en het proces waarmee we het voorkomen staat beschreven in ",[1332,2123,2125],{"href":2124},"\u002Fblog\u002Fbuilding-with-ai","hoe we met AI bouwen",". Dit artikel hoort bij onze video-aflevering over hetzelfde onderwerp; de vorige was ",[1332,2128,2130],{"href":2129},"\u002Fblog\u002Fssl-certificate-pinning-flutter","je SSL-pinning werkt waarschijnlijk niet",[175,2132,2133],{},"Doe de oefening en vertel ons hoeveel rijen je hebt weggestreept en op welk scherm. Onze inzet is dat de meeste mensen op dezelfde drie states uitkomen als wij.",[2135,2136,2137],"post-credits",{},[175,2138,2139,2140,2144,2145,2149],{},"Ilya Nixan is oprichter en lead developer bij ",[1332,2141,2143],{"href":2142},"\u002F","Nerdy Production",", een Flutter-first bureau dat apps bouwt en onderhoudt in fintech, zorg en retail. We doen ook ",[1332,2146,2148],{"href":2147},"\u002Fservices\u002Fai-code-audit","AI-codeaudits"," voor teams waarvan de codebase sneller groeide dan iemand kon reviewen.",[2151,2152,2153],"style",{},"html pre.shiki code .smGrS, html code.shiki .smGrS{--shiki-light:#39ADB5;--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sZMiF, html code.shiki .sZMiF{--shiki-light:#E2931D;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .su5hD, html code.shiki .su5hD{--shiki-light:#90A4AE;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sbsja, html code.shiki .sbsja{--shiki-light:#9C3EDA;--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sP7_E, html code.shiki .sP7_E{--shiki-light:#39ADB5;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .s_hVV, html code.shiki .s_hVV{--shiki-light:#90A4AE;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sGLFI, html code.shiki .sGLFI{--shiki-light:#6182B8;--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sVHd0, html code.shiki .sVHd0{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#D73A49;--shiki-default-font-style:inherit;--shiki-dark:#F97583;--shiki-dark-font-style:inherit}html pre.shiki code .sbgvK, html code.shiki .sbgvK{--shiki-light:#E2931D;--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .s39Yj, html code.shiki .s39Yj{--shiki-light:#39ADB5;--shiki-default:#005CC5;--shiki-dark:#79B8FF}",{"title":186,"searchDepth":209,"depth":209,"links":2155},[2156,2157,2161,2164,2167,2168,2171,2172,2173,2174],{"id":441,"depth":209,"text":442},{"id":512,"depth":209,"text":513,"children":2158},[2159,2160],{"id":540,"depth":238,"text":541},{"id":673,"depth":238,"text":674},{"id":713,"depth":209,"text":714,"children":2162},[2163],{"id":763,"depth":238,"text":764},{"id":925,"depth":209,"text":926,"children":2165},[2166],{"id":1311,"depth":238,"text":1312},{"id":1339,"depth":209,"text":1340},{"id":1695,"depth":209,"text":1696,"children":2169},[2170],{"id":1777,"depth":238,"text":1778},{"id":1854,"depth":209,"text":1855},{"id":2076,"depth":209,"text":2077},{"id":2093,"depth":209,"text":2094},{"id":2109,"depth":209,"text":2110},"md",{"type":2177,"url":2178},"youtube","https:\u002F\u002Fyoutu.be\u002FiUI7vWX_22w",{},"Een agent leverde een stateklasse met acht combinaties en drie die iets betekenen. Hoe sealed classes en volledigheid de andere vijf onbouwbaar maken.","Onmogelijke states in Dart: sealed classes en ADTs","Eén bool, twee nullables, acht combinaties, drie die iets betekenen. De andere vijf compileren. Zo zorg je dat ze dat niet meer doen.","\u002Fblog\u002Fimpossible-states-dart-sealed-classes",{"title":169,"description":177},"impossible-states-dart-sealed-classes","blog\u002Fimpossible-states-dart-sealed-classes","We gaven een agent een doodgewone taak — een productlijstscherm met laden, een fout en data — en kregen een klasse terug met één boolean en twee nullable velden. Dat zijn acht combinaties, waarvan er drie iets betekenen; de andere vijf compileren, komen door de review, gaan naar productie en blijven daar liggen. Dit is wat een onmogelijke state is, waarom dat hetzelfde is als een gebroken invariant, waarom de echte kosten niet de runtimebugs zijn maar de ongeschreven afspraken die elke lezer opnieuw moet reconstrueren, en hoe sealed classes samen met volledigheidscontrole een hele categorie fouten in een buildfout veranderen. Plus: waarom een enum met nullable velden het erger maakt, welke ene regel de controle stilzwijgend uitzet, en de vier plekken waar algebraïsche datatypes helemaal niet helpen.","2026-08-31T12:00:00Z","software-engineering",null,"Ug6o9YnH1nE9i7UMr3pM-eyGGNlYlu1UmdiwaeuOVUw",[2193,2250,2297,2347,2436,2488],{"id":2194,"bio":2195,"expertise":2200,"extension":2216,"links":2217,"meta":2221,"metaDescription":2222,"name":2227,"ogDescription":2230,"photo":2235,"role":2236,"seniority":2241,"skills":2242,"slug":2243,"specialization":2244,"stem":2243,"__hash__":2249},"team_members\u002Fdima.yaml",{"en":2196,"ru":2197,"es":2198,"nl":2199},"Dima has been building with [Flutter](\u002Ftechnologies\u002Fflutter) since 2021, and specializes in :term[state management]{slug=\"state-management\"} and app architecture.\n\nHe has hands-on experience with real-time communication protocols — :term{slug=\"webrtc\"} for audio and video, and :term{slug=\"xmpp\"} for messaging — which makes him comfortable with the network-heavy, stateful features many teams struggle to get right.\n","Дима работает с [Flutter](\u002Ftechnologies\u002Fflutter) с 2021 года и специализируется на :term[state management]{slug=\"state-management\"} и архитектуре приложений.\n\nУ него есть практический опыт с протоколами реального времени — :term{slug=\"webrtc\"} для аудио и видео и :term{slug=\"xmpp\"} для обмена сообщениями, — поэтому ему близки сетевые stateful-фичи, которые многим командам даются с трудом.\n","Dima trabaja con [Flutter](\u002Ftechnologies\u002Fflutter) desde 2021 y se especializa en :term[gestión de estado]{slug=\"state-management\"} y arquitectura de aplicaciones.\n\nTiene experiencia práctica con protocolos de comunicación en tiempo real —:term{slug=\"webrtc\"} para audio y vídeo, y :term{slug=\"xmpp\"} para mensajería—, lo que le hace sentirse cómodo con esas funcionalidades con mucho estado y mucha red que a muchos equipos se les atragantan.\n","Dima werkt sinds 2021 met [Flutter](\u002Ftechnologies\u002Fflutter) en is gespecialiseerd in :term[state management]{slug=\"state-management\"} en applicatiearchitectuur.\n\nHij heeft praktijkervaring met protocollen voor realtime communicatie — :term{slug=\"webrtc\"} voor audio en video, en :term{slug=\"xmpp\"} voor berichten — waardoor hij zich thuis voelt bij de netwerkzware functies met veel staat waar veel teams moeite mee hebben.\n",[2201,2204,2209,2211],{"en":2202,"ru":2202,"es":2203,"nl":2202},"State management","Gestión de estado",{"en":2205,"ru":2206,"es":2207,"nl":2208},"Application architecture","Архитектура приложений","Arquitectura de aplicaciones","Applicatiearchitectuur",{"en":2210,"ru":2210,"es":2210,"nl":2210},"WebRTC",{"en":2212,"ru":2213,"es":2214,"nl":2215},"XMPP messaging","Обмен сообщениями по XMPP","Mensajería XMPP","Berichten via XMPP","yaml",[2218],{"type":2219,"address":2220},"email","konopatov@nerdy.pro",{},{"en":2223,"ru":2224,"es":2225,"nl":2226},"Lead Flutter developer at Nerdy Production, building real-time apps since 2021 — state management, app architecture, WebRTC and XMPP.","Ведущий Flutter-разработчик Nerdy Production: приложения реального времени с 2021 года — state management, архитектура, WebRTC и XMPP.","Lead de desarrollo Flutter en Nerdy Production, construyendo apps en tiempo real desde 2021: gestión de estado, arquitectura de aplicaciones, WebRTC y XMPP.","Lead Flutter-developer bij Nerdy Production, bouwt sinds 2021 realtime-apps — state management, applicatiearchitectuur, WebRTC en XMPP.",{"en":2228,"ru":2229,"es":2228,"nl":2228},"Dima","Дима",{"en":2231,"ru":2232,"es":2233,"nl":2234},"Lead Flutter developer at Nerdy Production — real-time apps since 2021, and the state management that keeps them from falling over.","Ведущий Flutter-разработчик Nerdy Production: приложения реального времени с 2021 года и state management, на котором они держатся.","Lead de desarrollo Flutter en Nerdy Production: apps en tiempo real desde 2021 y la gestión de estado que evita que se caigan.","Lead Flutter-developer bij Nerdy Production — realtime-apps sinds 2021, en het state management dat ze overeind houdt.","\u002Fteam\u002Fdima.webp",{"en":2237,"ru":2238,"es":2239,"nl":2240},"Lead Flutter Developer","Ведущий Flutter-разработчик","Lead de desarrollo Flutter","Lead Flutter-developer","lead",[41,18,155,160,90,111,150,95],"dima",{"en":2245,"ru":2246,"es":2247,"nl":2248},"Real-time apps, state management, and architecture","Приложения реального времени, state management и архитектура","Apps en tiempo real, gestión de estado y arquitectura","Realtime-apps, state management en architectuur","1TuxUt3zjdVkQUvMf9dBITG872C0exrdZyyoKw1beiE",{"id":2251,"bio":2252,"expertise":2257,"extension":2216,"links":2190,"meta":2268,"metaDescription":2269,"name":2274,"ogDescription":2277,"photo":2282,"role":2283,"seniority":2288,"skills":2289,"slug":2290,"specialization":2291,"stem":2290,"__hash__":2296},"team_members\u002Fmasha.yaml",{"en":2253,"ru":2254,"es":2255,"nl":2256},"Masha builds [Flutter](\u002Ftechnologies\u002Fflutter) apps where design and copy are treated as one job.\n\nShe specializes in UI\u002FUX — turning product requirements into clean, usable interfaces — and in the writing inside the app, from onboarding flows to the microcopy that makes a screen make sense. The result is apps that feel considered, not just functional.\n","Маша делает приложения на [Flutter](\u002Ftechnologies\u002Fflutter), где дизайн и текст — одна задача.\n\nОна специализируется на UI\u002FUX, превращая продуктовые требования в чистые и удобные интерфейсы, и на текстах внутри приложения — от онбординга до микрокопирайта, который делает экран понятным. В итоге приложения получаются продуманными, а не просто рабочими.\n","Masha crea apps en [Flutter](\u002Ftechnologies\u002Fflutter) donde el diseño y el texto se tratan como un mismo trabajo.\n\nSe especializa en UI\u002FUX —convertir requisitos de producto en interfaces limpias y usables— y en la escritura dentro de la app, desde los flujos de onboarding hasta el microcopy que hace que una pantalla se entienda. El resultado son apps que se sienten pensadas, no solo funcionales.\n","Masha bouwt [Flutter](\u002Ftechnologies\u002Fflutter)-apps waarin ontwerp en tekst als één taak worden behandeld.\n\nZe is gespecialiseerd in UI\u002FUX — productwensen omzetten in heldere, bruikbare interfaces — en in de teksten binnen de app, van onboarding tot de microteksten die een scherm begrijpelijk maken. Het resultaat zijn apps die doordacht aanvoelen en niet alleen werken.\n",[2258,2263],{"en":2259,"ru":2260,"es":2261,"nl":2262},"UI\u002FUX design","UI\u002FUX-дизайн","Diseño UI\u002FUX","UI\u002FUX-ontwerp",{"en":2264,"ru":2265,"es":2266,"nl":2267},"Product copywriting","Продуктовый копирайтинг","Redacción de producto","Productteksten",{},{"en":2270,"ru":2271,"es":2272,"nl":2273},"Flutter developer at Nerdy Production working where design meets copy — UI\u002FUX, onboarding flows, and the microcopy that makes a screen make sense.","Flutter-разработчик Nerdy Production на стыке дизайна и текста: UI\u002FUX, онбординг и микрокопирайт, который делает экран понятным.","Desarrolladora Flutter en Nerdy Production, donde el diseño se encuentra con el texto: UI\u002FUX, onboarding y el microcopy que hace que una pantalla se entienda.","Flutter-developer bij Nerdy Production op het snijvlak van ontwerp en tekst — UI\u002FUX, onboarding, en de microteksten die een scherm begrijpelijk maken.",{"en":2275,"ru":2276,"es":2275,"nl":2275},"Masha","Маша",{"en":2278,"ru":2279,"es":2280,"nl":2281},"Flutter developer at Nerdy Production working where design meets copy — onboarding flows and the microcopy that makes a screen make sense.","Flutter-разработчик Nerdy Production на стыке дизайна и текста: онбординг и микрокопирайт, который делает экран понятным.","Desarrolladora Flutter en Nerdy Production, donde el diseño se encuentra con el texto: onboarding y el microcopy que hace entender una pantalla.","Flutter-developer bij Nerdy Production op het snijvlak van ontwerp en tekst — onboarding en de microteksten die een scherm begrijpelijk maken.","\u002Fteam\u002Fmasha.webp",{"en":2284,"ru":2285,"es":2286,"nl":2287},"Flutter Developer","Flutter-разработчик","Desarrolladora Flutter","Flutter-developer","middle",[41,18],"masha",{"en":2292,"ru":2293,"es":2294,"nl":2295},"UI\u002FUX and copywriting for Flutter apps","UI\u002FUX и копирайтинг для Flutter-приложений","UI\u002FUX y redacción para apps Flutter","UI\u002FUX en teksten voor Flutter-apps","z3c64iWR39RIrn8wE0elSy00IfCITYgNkbgp_G_sslk",{"id":2298,"bio":2299,"expertise":2304,"extension":2216,"links":2315,"meta":2318,"metaDescription":2319,"name":2324,"ogDescription":2327,"photo":2332,"role":2333,"seniority":2338,"skills":2339,"slug":2340,"specialization":2341,"stem":2340,"__hash__":2346},"team_members\u002Fmaxim.yaml",{"en":2300,"ru":2301,"es":2302,"nl":2303},"Maxim is a polyglot engineer who moves comfortably across [Go](\u002Ftechnologies\u002Fgo), [Flutter](\u002Ftechnologies\u002Fflutter), [Python](\u002Ftechnologies\u002Fpython), and [TypeScript](\u002Ftechnologies\u002Ftypescript), which lets him own a feature from the backend to the screen.\n\nHe came up at Ozon, one of Russia's largest marketplaces, where scale makes reliability non-negotiable — and it shows in his work: testing is a first-class part of how he builds, not an afterthought bolted on at the end.\n","Максим — полиглот-инженер, свободно работающий с [Go](\u002Ftechnologies\u002Fgo), [Flutter](\u002Ftechnologies\u002Fflutter), [Python](\u002Ftechnologies\u002Fpython) и [TypeScript](\u002Ftechnologies\u002Ftypescript), что позволяет ему вести фичу от бэкенда до экрана.\n\nОн вырос в Ozon, одном из крупнейших маркетплейсов России, где масштаб делает надёжность обязательной, — и это видно в его работе: тестирование для него первоклассная часть разработки, а не то, что прикручивают в конце.\n","Maxim es un ingeniero políglota que se mueve con soltura entre [Go](\u002Ftechnologies\u002Fgo), [Flutter](\u002Ftechnologies\u002Fflutter), [Python](\u002Ftechnologies\u002Fpython) y [TypeScript](\u002Ftechnologies\u002Ftypescript), lo que le permite hacerse cargo de una funcionalidad desde el backend hasta la pantalla.\n\nSe formó en Ozon, uno de los mayores marketplaces de Rusia, donde la escala hace que la fiabilidad no sea negociable, y se le nota: el testing es para él una parte de primera clase de cómo construye, no un añadido al final.\n","Maxim is een polyglotte engineer die zich moeiteloos beweegt tussen [Go](\u002Ftechnologies\u002Fgo), [Flutter](\u002Ftechnologies\u002Fflutter), [Python](\u002Ftechnologies\u002Fpython) en [TypeScript](\u002Ftechnologies\u002Ftypescript), waardoor hij een functie van de backend tot het scherm kan dragen.\n\nHij is opgegroeid bij Ozon, een van de grootste marktplaatsen van Rusland, waar schaal betrouwbaarheid onderhandelbaar maakt noch toestaat — en dat zie je terug in zijn werk: testen is bij hem een volwaardig onderdeel van hoe hij bouwt, geen bijzaak die er aan het eind bij komt.\n",[2305,2310],{"en":2306,"ru":2307,"es":2308,"nl":2309},"Automated testing","Автоматизированное тестирование","Testing automatizado","Geautomatiseerd testen",{"en":2311,"ru":2312,"es":2313,"nl":2314},"High-load backend systems","Высоконагруженные бэкенд-системы","Sistemas backend de alta carga","Backendsystemen met hoge belasting",[2316],{"type":2219,"address":2317},"maxim@nerdy.pro",{},{"en":2320,"ru":2321,"es":2322,"nl":2323},"Senior engineer at Nerdy Production across Go, Flutter, Python and TypeScript — from backend to screen, with automated testing built in.","Старший инженер Nerdy Production: Go, Flutter, Python и TypeScript — от бэкенда до экрана, с автотестами как частью разработки.","Ingeniero senior en Nerdy Production con Go, Flutter, Python y TypeScript: del backend a la pantalla, con testing automatizado incorporado.","Senior engineer bij Nerdy Production met Go, Flutter, Python en TypeScript — van backend tot scherm, met geautomatiseerd testen ingebouwd.",{"en":2325,"ru":2326,"es":2325,"nl":2325},"Maxim","Максим",{"en":2328,"ru":2329,"es":2330,"nl":2331},"Senior engineer at Nerdy Production working from backend to screen — Go, Flutter, Python and TypeScript, with the tests written as he goes.","Старший инженер Nerdy Production, работающий от бэкенда до экрана: Go, Flutter, Python и TypeScript — и тесты, которые пишутся по ходу дела.","Ingeniero senior en Nerdy Production, del backend a la pantalla: Go, Flutter, Python y TypeScript, con los tests escritos sobre la marcha.","Senior engineer bij Nerdy Production, van backend tot scherm — Go, Flutter, Python en TypeScript, met de tests die hij onderweg schrijft.","\u002Fteam\u002Fmax.webp",{"en":2334,"ru":2335,"es":2336,"nl":2337},"Senior Software Engineer","Старший инженер-программист","Ingeniero de software senior","Senior software engineer","senior",[41,18,51,106,155,13,150],"maxim",{"en":2342,"ru":2343,"es":2344,"nl":2345},"Backend and Flutter engineering with a testing focus","Бэкенд и Flutter с фокусом на тестирование","Ingeniería backend y Flutter con foco en testing","Backend- en Flutter-engineering met focus op testen","a-8Kx1i-PQXIGU92_Rimqr3bPEur6ZwMfPKQWr2uk4I",{"id":2348,"bio":2349,"expertise":2354,"extension":2216,"links":2396,"meta":2408,"metaDescription":2409,"name":2414,"ogDescription":2417,"photo":2422,"role":2423,"seniority":2428,"skills":2429,"slug":170,"specialization":2430,"stem":170,"__hash__":2435},"team_members\u002Fnixan.yaml",{"en":2350,"ru":2351,"es":2352,"nl":2353},"Ilya founded Nerdy Production and leads its engineering. He has been building software since 2010 and shipping production Flutter since 2018.\n\nBefore that he was CTO of QIWI, one of Russia's largest payment platforms, where he ran roughly 12 engineering teams spanning web products down to card processing, :term{slug=\"pci-dss\"} scope, and contactless payments — including building contactless card payments on Android via :term[Host Card Emulation]{slug=\"host-card-emulation\"} over ISO\u002FIEC 14443, with EMV Contactless (Visa PayWave) on top.\n\nHe was also a principal developer at Yandex, where he worked on Yandex.Auto — taking native Android deep into the vehicle, with heavy CAN-bus integration through a custom CAN shield — and a principal at Evotor, whose point-of-sale devices run on a forked :term{slug=\"aosp\"}, giving him a low-level view of Android most app developers never touch.\n\nToday he leads delivery on the agency's flagship apps — from the chart-heavy fintech UI of [ExtraETF](\u002Fportfolio\u002Fextraetf) to the fully custom design system of [Arcana](\u002Fportfolio\u002Farcana). He writes most of the essays on this blog and maintains the agency's open-source work, including the [dxpdf](\u002Fopen-source\u002Fdxpdf) DOCX-to-PDF engine.\n\nHe works across [Flutter](\u002Ftechnologies\u002Fflutter), native iOS and Android, [Go](\u002Ftechnologies\u002Fgo), [Rust](\u002Ftechnologies\u002Frust), [TypeScript](\u002Ftechnologies\u002Ftypescript), [Kotlin](\u002Ftechnologies\u002Fkotlin), [Kubernetes](\u002Ftechnologies\u002Fkubernetes), and [Docker](\u002Ftechnologies\u002Fdocker), with a focus on app architecture, cross-platform delivery, and building teams that ship.\n","Илья основал Nerdy Production и руководит инженерной командой. Он занимается разработкой с 2010 года и выпускает продакшн-приложения на Flutter с 2018-го.\n\nДо этого он был CTO QIWI — одной из крупнейших платёжных платформ России, — где руководил примерно 12 инженерными командами: от веб-продуктов до карточного процессинга, зоны :term{slug=\"pci-dss\"} и бесконтактных платежей, включая бесконтактную оплату картой на Android через :term[Host Card Emulation]{slug=\"host-card-emulation\"} поверх ISO\u002FIEC 14443, с платёжным протоколом EMV Contactless (Visa PayWave).\n\nОн также был принципал-разработчиком в Яндексе, где работал над Яндекс.Авто, уводя нативный Android глубоко в автомобиль, с серьёзной интеграцией по шине CAN через собственный CAN-шилд, и принципалом в Эвоторе, чьи кассовые устройства работают на форке :term{slug=\"aosp\"}, что дало ему низкоуровневый взгляд на Android, недоступный большинству прикладных разработчиков.\n\nСейчас он ведёт поставку флагманских приложений агентства — от насыщенного графиками финтех-интерфейса [ExtraETF](\u002Fportfolio\u002Fextraetf) до полностью кастомной дизайн-системы [Arcana](\u002Fportfolio\u002Farcana). Он пишет большую часть материалов этого блога и поддерживает open-source агентства, включая движок [dxpdf](\u002Fopen-source\u002Fdxpdf) для конвертации DOCX в PDF.\n\nРаботает с [Flutter](\u002Ftechnologies\u002Fflutter), нативными iOS и Android, [Go](\u002Ftechnologies\u002Fgo), [Rust](\u002Ftechnologies\u002Frust), [TypeScript](\u002Ftechnologies\u002Ftypescript), [Kotlin](\u002Ftechnologies\u002Fkotlin), [Kubernetes](\u002Ftechnologies\u002Fkubernetes) и [Docker](\u002Ftechnologies\u002Fdocker); его фокус — архитектура приложений, кросс-платформенная поставка и построение команд, которые доводят продукт до релиза.\n","Ilya fundó Nerdy Production y dirige su ingeniería. Lleva construyendo software desde 2010 y entregando Flutter en producción desde 2018.\n\nAntes fue CTO de QIWI, una de las mayores plataformas de pago de Rusia, donde dirigió alrededor de 12 equipos de ingeniería que abarcaban desde productos web hasta procesamiento de tarjetas, alcance :term{slug=\"pci-dss\"} y pagos contactless, incluida la construcción de pagos contactless con tarjeta en Android mediante :term[Host Card Emulation]{slug=\"host-card-emulation\"} sobre ISO\u002FIEC 14443, con EMV Contactless (Visa PayWave) por encima.\n\nTambién fue principal developer en Yandex, donde trabajó en Yandex.Auto —llevando Android nativo hasta el interior del vehículo, con una integración intensiva por bus CAN a través de un CAN shield propio— y principal en Evotor, cuyos terminales de punto de venta funcionan sobre un fork de :term{slug=\"aosp\"}, lo que le dio una visión de bajo nivel de Android que la mayoría de desarrolladores de apps nunca llega a tocar.\n\nHoy lidera la entrega de las apps insignia de la agencia, desde la interfaz fintech cargada de gráficos de [ExtraETF](\u002Fportfolio\u002Fextraetf) hasta el sistema de diseño totalmente a medida de [Arcana](\u002Fportfolio\u002Farcana). Escribe la mayoría de los artículos de este blog y mantiene el trabajo de código abierto de la agencia, incluido el motor de conversión de DOCX a PDF [dxpdf](\u002Fopen-source\u002Fdxpdf).\n\nTrabaja con [Flutter](\u002Ftechnologies\u002Fflutter), iOS y Android nativos, [Go](\u002Ftechnologies\u002Fgo), [Rust](\u002Ftechnologies\u002Frust), [TypeScript](\u002Ftechnologies\u002Ftypescript), [Kotlin](\u002Ftechnologies\u002Fkotlin), [Kubernetes](\u002Ftechnologies\u002Fkubernetes) y [Docker](\u002Ftechnologies\u002Fdocker), con foco en arquitectura de aplicaciones, entrega multiplataforma y la construcción de equipos que entregan.\n","Ilya richtte Nerdy Production op en leidt de engineering. Hij bouwt software sinds 2010 en levert sinds 2018 Flutter in productie.\n\nDaarvoor was hij CTO van QIWI, een van de grootste betaalplatforms van Rusland, waar hij zo'n 12 engineeringteams aanstuurde die reikten van webproducten tot kaartverwerking, :term{slug=\"pci-dss\"}-scope en contactloos betalen — waaronder het bouwen van contactloze kaartbetalingen op Android via :term[Host Card Emulation]{slug=\"host-card-emulation\"} over ISO\u002FIEC 14443, met EMV Contactless (Visa PayWave) daarbovenop.\n\nHij was ook principal developer bij Yandex, waar hij aan Yandex.Auto werkte — native Android diep de auto in brengen, met zware integratie over de CAN-bus via een eigen CAN-shield — en principal bij Evotor, waarvan de kassa-apparaten op een fork van :term{slug=\"aosp\"} draaien, wat hem een blik op Android op laag niveau gaf die de meeste app-ontwikkelaars nooit krijgen.\n\nVandaag leidt hij de oplevering van de vlaggenschipapps van het bureau — van de grafiekzware fintech-UI van [ExtraETF](\u002Fportfolio\u002Fextraetf) tot het volledig eigen designsysteem van [Arcana](\u002Fportfolio\u002Farcana). Hij schrijft de meeste artikelen op deze blog en onderhoudt het opensourcewerk van het bureau, waaronder de DOCX-naar-PDF-motor [dxpdf](\u002Fopen-source\u002Fdxpdf).\n\nHij werkt met [Flutter](\u002Ftechnologies\u002Fflutter), native iOS en Android, [Go](\u002Ftechnologies\u002Fgo), [Rust](\u002Ftechnologies\u002Frust), [TypeScript](\u002Ftechnologies\u002Ftypescript), [Kotlin](\u002Ftechnologies\u002Fkotlin), [Kubernetes](\u002Ftechnologies\u002Fkubernetes) en [Docker](\u002Ftechnologies\u002Fdocker), met de nadruk op applicatiearchitectuur, cross-platform oplevering en het bouwen van teams die opleveren.\n",[2355,2360,2365,2370,2375,2380,2385,2390,2392,2394],{"en":2356,"ru":2357,"es":2358,"nl":2359},"iOS development","Разработка под iOS","Desarrollo iOS","iOS-ontwikkeling",{"en":2361,"ru":2362,"es":2363,"nl":2364},"Software architecture","Архитектура ПО","Arquitectura de software","Softwarearchitectuur",{"en":2366,"ru":2367,"es":2368,"nl":2369},"Engineering team leadership","Руководство инженерными командами","Liderazgo de equipos de ingeniería","Leidinggeven aan engineeringteams",{"en":2371,"ru":2372,"es":2373,"nl":2374},"Payment systems","Платёжные системы","Sistemas de pago","Betaalsystemen",{"en":2376,"ru":2377,"es":2378,"nl":2379},"Card processing","Карточный процессинг","Procesamiento de tarjetas","Kaartverwerking",{"en":2381,"ru":2382,"es":2383,"nl":2384},"PCI-DSS compliance","Соответствие PCI-DSS","Cumplimiento de PCI-DSS","Naleving van PCI-DSS",{"en":2386,"ru":2387,"es":2388,"nl":2389},"NFC and contactless payments","NFC и бесконтактные платежи","NFC y pagos contactless","NFC en contactloos betalen",{"en":2391,"ru":2391,"es":2391,"nl":2391},"Host Card Emulation",{"en":2393,"ru":2393,"es":2393,"nl":2393},"EMV Contactless",{"en":2395,"ru":2395,"es":2395,"nl":2395},"AOSP",[2397,2400,2403,2406],{"type":2398,"url":2399},"github","https:\u002F\u002Fgithub.com\u002Fthenixan",{"type":2401,"url":2402},"linkedin","https:\u002F\u002Fwww.linkedin.com\u002Fin\u002Fthenixan\u002F",{"type":2404,"url":2405},"telegram","https:\u002F\u002Ft.me\u002Fthenixan",{"type":2219,"address":2407},"nixan@nerdy.pro",{},{"en":2410,"ru":2411,"es":2412,"nl":2413},"Founder and lead developer at Nerdy Production, former CTO of QIWI. Flutter architecture, payments infrastructure, and engineering teams that ship.","Основатель и ведущий разработчик Nerdy Production, экс-CTO QIWI. Архитектура на Flutter, платёжная инфраструктура и команды, которые доводят до релиза.","Fundador y lead developer en Nerdy Production, ex-CTO de QIWI. Arquitectura Flutter, infraestructura de pagos y equipos de ingeniería que entregan.","Oprichter en lead developer bij Nerdy Production, oud-CTO van QIWI. Flutter-architectuur, betaalinfrastructuur en teams die opleveren.",{"en":2415,"ru":2416,"es":2415,"nl":2415},"Ilya Nixan","Илья Никсан",{"en":2418,"ru":2419,"es":2420,"nl":2421},"Founder and lead developer at Nerdy Production, former CTO of QIWI — Flutter architecture, payments infrastructure, and teams that ship.","Основатель и ведущий разработчик Nerdy Production, экс-CTO QIWI: архитектура на Flutter, платёжная инфраструктура и команды, которые доводят до релиза.","Fundador y lead developer en Nerdy Production, ex-CTO de QIWI: arquitectura Flutter, infraestructura de pagos y equipos que entregan.","Oprichter en lead developer bij Nerdy Production, oud-CTO van QIWI — Flutter-architectuur, betaalinfrastructuur en teams die opleveren.","\u002Fteam\u002Fnixan.webp",{"en":2424,"ru":2425,"es":2426,"nl":2427},"Founder & Lead Developer","Основатель и ведущий разработчик","Fundador y lead developer","Oprichter & lead developer","founder_lead",[41,18,71,141,76,146,51,126,121,155,160,90,13,150,136,7,29,80],{"en":2431,"ru":2432,"es":2433,"nl":2434},"Flutter architecture and leading delivery teams","Архитектура на Flutter и руководство командами поставки","Arquitectura Flutter y liderazgo de equipos de entrega","Flutter-architectuur en het leiden van opleverteams","BmeQZ7JyhRFIx8527WO2t1-WklP8AAvevAxiN9U4TEs",{"id":2437,"bio":2438,"expertise":2443,"extension":2216,"links":2455,"meta":2460,"metaDescription":2461,"name":2466,"ogDescription":2469,"photo":2474,"role":2475,"seniority":2338,"skills":2480,"slug":2481,"specialization":2482,"stem":2481,"__hash__":2487},"team_members\u002Froma.yaml",{"en":2439,"ru":2440,"es":2441,"nl":2442},"Roman works across native iOS and Android and [Flutter](\u002Ftechnologies\u002Fflutter), and pairs that mobile depth with [Python](\u002Ftechnologies\u002Fpython) and applied AI.\n\nMuch of his background is in ERP and CRM development — complex, data-heavy business systems where getting the domain model right matters more than the UI — which gives him a pragmatic eye for how an app fits the process behind it.\n","Роман работает с нативными iOS и Android и с [Flutter](\u002Ftechnologies\u002Fflutter), дополняя мобильную экспертизу [Python](\u002Ftechnologies\u002Fpython) и прикладным AI.\n\nЗначительная часть его опыта — разработка ERP и CRM, сложных систем с большим объёмом данных, где правильная доменная модель важнее интерфейса, — что даёт ему прагматичный взгляд на то, как приложение встраивается в процесс за ним.\n","Roman trabaja con iOS y Android nativos y con [Flutter](\u002Ftechnologies\u002Fflutter), y combina esa profundidad móvil con [Python](\u002Ftechnologies\u002Fpython) e IA aplicada.\n\nBuena parte de su trayectoria está en el desarrollo de ERP y CRM —sistemas de negocio complejos y con mucho dato, donde acertar con el modelo de dominio importa más que la interfaz—, lo que le da una mirada pragmática sobre cómo encaja una app en el proceso que hay detrás.\n","Roman werkt met native iOS en Android en met [Flutter](\u002Ftechnologies\u002Fflutter), en combineert die mobiele diepgang met [Python](\u002Ftechnologies\u002Fpython) en toegepaste AI.\n\nEen groot deel van zijn achtergrond ligt in ERP- en CRM-ontwikkeling — complexe, datazware bedrijfssystemen waarin het domeinmodel goed krijgen zwaarder weegt dan de interface — wat hem een pragmatische blik geeft op hoe een app in het proces erachter past.\n",[2444,2445,2450],{"en":2356,"ru":2357,"es":2358,"nl":2359},{"en":2446,"ru":2447,"es":2448,"nl":2449},"Applied AI","Прикладной AI","IA aplicada","Toegepaste AI",{"en":2451,"ru":2452,"es":2453,"nl":2454},"ERP and CRM systems","ERP- и CRM-системы","Sistemas ERP y CRM","ERP- en CRM-systemen",[2456,2458],{"type":2401,"url":2457},"https:\u002F\u002Fwww.linkedin.com\u002Fin\u002Fromanbatler\u002F",{"type":2219,"address":2459},"roma@nerdy.pro",{},{"en":2462,"ru":2463,"es":2464,"nl":2465},"Senior mobile developer at Nerdy Production — native iOS and Android, Flutter, applied AI, and a background in data-heavy ERP and CRM systems.","Старший мобильный разработчик Nerdy Production: нативные iOS и Android, Flutter, прикладной AI и опыт в ERP- и CRM-системах.","Desarrollador móvil senior en Nerdy Production: iOS y Android nativos, Flutter, IA aplicada y experiencia en sistemas ERP y CRM con gran volumen de datos.","Senior mobiele developer bij Nerdy Production — native iOS en Android, Flutter, toegepaste AI, en een achtergrond in datazware ERP- en CRM-systemen.",{"en":2467,"ru":2468,"es":2467,"nl":2467},"Roman","Рома",{"en":2470,"ru":2471,"es":2472,"nl":2473},"Senior mobile developer at Nerdy Production — native iOS and Android, Flutter, applied AI, and years of data-heavy ERP and CRM behind him.","Старший мобильный разработчик Nerdy Production: нативные iOS и Android, Flutter, прикладной AI и годы работы с нагруженными ERP и CRM.","Desarrollador móvil senior en Nerdy Production: iOS y Android nativos, Flutter, IA aplicada y años de ERP y CRM con mucha carga de datos.","Senior mobiele developer bij Nerdy Production — native iOS en Android, Flutter, toegepaste AI, en jaren datazware ERP en CRM achter zich.","\u002Fteam\u002Froma.webp",{"en":2476,"ru":2477,"es":2478,"nl":2479},"Senior Mobile Developer","Старший мобильный разработчик","Desarrollador móvil senior","Senior mobiele developer",[7,41,18,141,146,106],"roma",{"en":2483,"ru":2484,"es":2485,"nl":2486},"Mobile, AI, and ERP\u002FCRM systems","Мобильная разработка, AI и системы ERP\u002FCRM","Móvil, IA y sistemas ERP\u002FCRM","Mobiel, AI en ERP\u002FCRM-systemen","caPSPO2BX35G9lEjNb6p3U4uF_V7yh8cexCe6l5hm8Y",{"id":2489,"bio":2490,"expertise":2495,"extension":2216,"links":2190,"meta":2506,"metaDescription":2507,"name":2512,"ogDescription":2515,"photo":2520,"role":2521,"seniority":2338,"skills":2526,"slug":2527,"specialization":2528,"stem":2527,"__hash__":2533},"team_members\u002Fxsox.yaml",{"en":2491,"ru":2492,"es":2493,"nl":2494},"Eugene builds the systems that apps depend on. His background is in backend engineering — designing and running the APIs, data models, and services behind a product, primarily in [Python](\u002Ftechnologies\u002Fpython) and [Django](\u002Ftechnologies\u002Fdjango).\n\nHe also works in [Flutter](\u002Ftechnologies\u002Fflutter), so he can reason about a feature from the database to the device and build the backend so the app that consumes it stays simple.\n","Женя строит системы, на которые опираются приложения. Его основной опыт — бэкенд-инженерия: проектирование и эксплуатация API, моделей данных и сервисов за продуктом, в первую очередь на [Python](\u002Ftechnologies\u002Fpython) и [Django](\u002Ftechnologies\u002Fdjango).\n\nОн также работает с [Flutter](\u002Ftechnologies\u002Fflutter), поэтому может рассуждать о фиче от базы данных до устройства и строить бэкенд так, чтобы потребляющее его приложение оставалось простым.\n","Eugene construye los sistemas de los que dependen las apps. Su trayectoria está en la ingeniería backend: diseñar y operar las APIs, los modelos de datos y los servicios que hay detrás de un producto, principalmente en [Python](\u002Ftechnologies\u002Fpython) y [Django](\u002Ftechnologies\u002Fdjango).\n\nTambién trabaja con [Flutter](\u002Ftechnologies\u002Fflutter), así que puede razonar sobre una funcionalidad desde la base de datos hasta el dispositivo y construir el backend de forma que la app que lo consume siga siendo simple.\n","Eugene bouwt de systemen waar apps op leunen. Zijn achtergrond ligt in backend-engineering: de API's, datamodellen en services achter een product ontwerpen en draaien, vooral in [Python](\u002Ftechnologies\u002Fpython) en [Django](\u002Ftechnologies\u002Fdjango).\n\nHij werkt daarnaast met [Flutter](\u002Ftechnologies\u002Fflutter), zodat hij over een functie kan nadenken van de database tot het toestel en de backend zo kan bouwen dat de app die hem verbruikt eenvoudig blijft.\n",[2496,2501],{"en":2497,"ru":2498,"es":2499,"nl":2500},"API design","Проектирование API","Diseño de APIs","API-ontwerp",{"en":2502,"ru":2503,"es":2504,"nl":2505},"Data modelling","Моделирование данных","Modelado de datos","Datamodellering",{},{"en":2508,"ru":2509,"es":2510,"nl":2511},"Senior backend developer at Nerdy Production building the APIs, data models and services behind our apps, primarily in Python and Django.","Старший бэкенд-разработчик Nerdy Production: API, модели данных и сервисы за нашими приложениями, в первую очередь на Python и Django.","Desarrollador backend senior en Nerdy Production: las APIs, modelos de datos y servicios detrás de nuestras apps, sobre todo en Python y Django.","Senior backenddeveloper bij Nerdy Production die de API's, datamodellen en services achter onze apps bouwt, vooral in Python en Django.",{"en":2513,"ru":2514,"es":2513,"nl":2513},"Eugene Xsox","Женя Xsox",{"en":2516,"ru":2517,"es":2518,"nl":2519},"Senior backend developer at Nerdy Production — the APIs, data models and services our apps run on, mostly in Python and Django.","Старший бэкенд-разработчик Nerdy Production: API, модели данных и сервисы, на которых работают наши приложения, в основном на Python и Django.","Desarrollador backend senior en Nerdy Production: las APIs, modelos de datos y servicios sobre los que corren nuestras apps, en Python y Django.","Senior backenddeveloper bij Nerdy Production — de API's, datamodellen en services waarop onze apps draaien, vooral in Python en Django.","\u002Fteam\u002Fxsox.webp",{"en":2522,"ru":2523,"es":2524,"nl":2525},"Senior Backend Developer","Старший бэкенд-разработчик","Desarrollador backend senior","Senior backenddeveloper",[106,24,41,18,155,160,90,150],"xsox",{"en":2529,"ru":2530,"es":2531,"nl":2532},"Python\u002FDjango backends, plus Flutter","Бэкенды на Python\u002FDjango и Flutter","Backends en Python\u002FDjango, además de Flutter","Backends in Python\u002FDjango, plus Flutter","w_QAKKq_Mh_dtMvAxFpAJBC10PW1I7zgVOxELwRHwO8",[2535,2544,2551,2556,2566],{"id":2536,"extension":2216,"meta":2537,"name":2538,"slug":2542,"stem":2542,"__hash__":2543},"blog_topics\u002Fcybersecurity.yaml",{},{"en":2539,"ru":2540,"es":2541,"nl":2539},"Cybersecurity","Кибербезопасность","Ciberseguridad","cybersecurity","kt1rdsMy5W4b9MvGHm6mZVCr0inHidEuqaosPYmmk4E",{"id":2545,"extension":2216,"meta":2546,"name":2547,"slug":2549,"stem":2549,"__hash__":2550},"blog_topics\u002Fdevops.yaml",{},{"en":2548,"ru":2548,"es":2548,"nl":2548},"DevOps","devops","D3mxlZuFHKCGeszKZuWCiQmHmP5EiCvzg1qEAIQhnyU",{"id":2552,"extension":2216,"meta":2553,"name":2554,"slug":41,"stem":41,"__hash__":2555},"blog_topics\u002Fflutter.yaml",{},{"en":39,"ru":39,"es":39,"nl":39},"OOeALAmwFuEqByuJvTGk4g15FIHENPfYMdpfZh_HTAE",{"id":2557,"extension":2216,"meta":2558,"name":2559,"slug":2564,"stem":2564,"__hash__":2565},"blog_topics\u002Ffor-founders.yaml",{},{"en":2560,"ru":2561,"es":2562,"nl":2563},"For Founders","Для основателей","Para fundadores","Voor oprichters","for-founders","lp2u6Ol2qF4DGxpB83HxbbGzWioT-e40mJhD4lr28-U",{"id":2567,"extension":2216,"meta":2568,"name":2569,"slug":2189,"stem":2189,"__hash__":2574},"blog_topics\u002Fsoftware-engineering.yaml",{},{"en":2570,"ru":2571,"es":2572,"nl":2573},"Software Engineering","Разработка ПО","Ingeniería de software","Software-engineering","aZMb3aUDptoFQk_cEE6l5H4wrYcgZuBYUubmKqOAaaU",{"data":2576,"body":2577},{},{"type":2578,"children":2579},"root",[2580,2586,2605,2616,2644],{"type":2581,"tag":175,"props":2582,"children":2583},"element",{},[2584],{"type":1742,"value":2585},"Ilya richtte Nerdy Production op en leidt de engineering. Hij bouwt software sinds 2010 en levert sinds 2018 Flutter in productie.",{"type":2581,"tag":175,"props":2587,"children":2588},{},[2589,2591,2596,2598,2603],{"type":1742,"value":2590},"Daarvoor was hij CTO van QIWI, een van de grootste betaalplatforms van Rusland, waar hij zo'n 12 engineeringteams aanstuurde die reikten van webproducten tot kaartverwerking, ",{"type":2581,"tag":2592,"props":2593,"children":2595},"term",{"slug":2594},"pci-dss",[],{"type":1742,"value":2597},"-scope en contactloos betalen — waaronder het bouwen van contactloze kaartbetalingen op Android via ",{"type":2581,"tag":2592,"props":2599,"children":2601},{"slug":2600},"host-card-emulation",[2602],{"type":1742,"value":2391},{"type":1742,"value":2604}," over ISO\u002FIEC 14443, met EMV Contactless (Visa PayWave) daarbovenop.",{"type":2581,"tag":175,"props":2606,"children":2607},{},[2608,2610,2614],{"type":1742,"value":2609},"Hij was ook principal developer bij Yandex, waar hij aan Yandex.Auto werkte — native Android diep de auto in brengen, met zware integratie over de CAN-bus via een eigen CAN-shield — en principal bij Evotor, waarvan de kassa-apparaten op een fork van ",{"type":2581,"tag":2592,"props":2611,"children":2613},{"slug":2612},"aosp",[],{"type":1742,"value":2615}," draaien, wat hem een blik op Android op laag niveau gaf die de meeste app-ontwikkelaars nooit krijgen.",{"type":2581,"tag":175,"props":2617,"children":2618},{},[2619,2621,2627,2629,2635,2637,2643],{"type":1742,"value":2620},"Vandaag leidt hij de oplevering van de vlaggenschipapps van het bureau — van de grafiekzware fintech-UI van ",{"type":2581,"tag":1332,"props":2622,"children":2624},{"href":2623},"\u002Fportfolio\u002Fextraetf",[2625],{"type":1742,"value":2626},"ExtraETF",{"type":1742,"value":2628}," tot het volledig eigen designsysteem van ",{"type":2581,"tag":1332,"props":2630,"children":2632},{"href":2631},"\u002Fportfolio\u002Farcana",[2633],{"type":1742,"value":2634},"Arcana",{"type":1742,"value":2636},". Hij schrijft de meeste artikelen op deze blog en onderhoudt het opensourcewerk van het bureau, waaronder de DOCX-naar-PDF-motor ",{"type":2581,"tag":1332,"props":2638,"children":2640},{"href":2639},"\u002Fopen-source\u002Fdxpdf",[2641],{"type":1742,"value":2642},"dxpdf",{"type":1742,"value":487},{"type":2581,"tag":175,"props":2645,"children":2646},{},[2647,2649,2654,2656,2661,2662,2667,2668,2673,2674,2679,2680,2685,2686,2691],{"type":1742,"value":2648},"Hij werkt met ",{"type":2581,"tag":1332,"props":2650,"children":2652},{"href":2651},"\u002Ftechnologies\u002Fflutter",[2653],{"type":1742,"value":39},{"type":1742,"value":2655},", native iOS en Android, ",{"type":2581,"tag":1332,"props":2657,"children":2659},{"href":2658},"\u002Ftechnologies\u002Fgo",[2660],{"type":1742,"value":49},{"type":1742,"value":1893},{"type":2581,"tag":1332,"props":2663,"children":2665},{"href":2664},"\u002Ftechnologies\u002Frust",[2666],{"type":1742,"value":124},{"type":1742,"value":1893},{"type":2581,"tag":1332,"props":2669,"children":2671},{"href":2670},"\u002Ftechnologies\u002Ftypescript",[2672],{"type":1742,"value":153},{"type":1742,"value":1893},{"type":2581,"tag":1332,"props":2675,"children":2677},{"href":2676},"\u002Ftechnologies\u002Fkotlin",[2678],{"type":1742,"value":69},{"type":1742,"value":1893},{"type":2581,"tag":1332,"props":2681,"children":2683},{"href":2682},"\u002Ftechnologies\u002Fkubernetes",[2684],{"type":1742,"value":78},{"type":1742,"value":566},{"type":2581,"tag":1332,"props":2687,"children":2689},{"href":2688},"\u002Ftechnologies\u002Fdocker",[2690],{"type":1742,"value":27},{"type":1742,"value":2692},", met de nadruk op applicatiearchitectuur, cross-platform oplevering en het bouwen van teams die opleveren.",[2694,2706,2726,2739,2753,2767,2781,2798,2811,2826,2840,2853,2868,2882,2898,2908,2921,2932,2942,2955,2965,2976,2991,3000,3013,3027,3034,3046,3059,3070,3085,3098,3111,3124,3137,3150,3163,3174,3185,3197,3208,3220,3230,3242,3255,3267,3279,3293,3304,3316,3329,3338,3350],{"slug":2612,"term":2395,"definition":2695,"category":2696,"aliases":2697,"links":2699,"related":2703,"readMore":-1,"target":2704,"hasArticle":2705},"Het Android Open Source Project — Android zonder de Google-laag erbovenop, door iedereen vrij te forken. Kassaterminals, kiosken en autosystemen draaien op forks ervan, en werken op dat niveau legt delen van het OS bloot die een app-ontwikkelaar nooit ziet.","platform",[2698],"Android Open Source Project",[2700],{"kind":2701,"url":2702},"website","https:\u002F\u002Fsource.android.com\u002F",[2600],"\u002Fglossary#aosp",false,{"slug":2707,"term":2708,"definition":2709,"category":2710,"aliases":2711,"links":2716,"related":2722,"readMore":-1,"target":2725,"hasArticle":2705},"gdpr","AVG","De EU-verordening over persoonsgegevens van mensen in de EU: een rechtsgrond om ze te verzamelen, echte toestemming voor tracking, en het recht om ze in te zien en te laten wissen. Ze volgt je gebruikers, niet je servers, en geldt dus ongeacht waar het bedrijf staat ingeschreven.","practice",[2712,2713,2714,2715],"GDPR","General Data Protection Regulation","Algemene verordening gegevensbescherming","gegevensbescherming",[2717,2719],{"kind":2701,"url":2718},"https:\u002F\u002Fgdpr.eu\u002F",{"kind":2720,"url":2721},"wikipedia","https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FGeneral_Data_Protection_Regulation",[2594,2723,2724],"soc-2","hipaa","\u002Fglossary#gdpr",{"slug":2727,"term":2728,"definition":2729,"category":2696,"aliases":2730,"links":2731,"related":2735,"readMore":-1,"target":2738,"hasArticle":2705},"app-clips","App Clips","Een Apple-functie die een klein stuk van een iOS-app draait — onder de 15 MB — zonder het geheel te installeren. Te starten via een QR-code, een NFC-tag of een link, voor als de eerste handeling van een gebruiker niet eerst een gang naar de App Store zou moeten vergen.",[],[2732],{"kind":2733,"url":2734},"documentation","https:\u002F\u002Fdeveloper.apple.com\u002Fapp-clips\u002F",[2736,2737],"deep-linking","install-referrer","\u002Fglossary#app-clips",{"slug":2740,"term":2741,"definition":2742,"category":2696,"aliases":2743,"links":2744,"related":2749,"readMore":-1,"target":2752,"hasArticle":2705},"bigquery","BigQuery","Het analytische datawarehouse van Google Cloud. Je laat SQL los op miljarden rijen en het scant ze in seconden, op opslag die losstaat van de machines die de query uitvoeren — en juist dat houdt rapportage en verkenning weg bij de database die het live verkeer bedient.",[],[2745,2747],{"kind":2701,"url":2746},"https:\u002F\u002Fcloud.google.com\u002Fbigquery",{"kind":2720,"url":2748},"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FBigQuery",[2750,2751],"object-storage","elasticsearch","\u002Fglossary#bigquery",{"slug":2754,"term":2755,"definition":2756,"category":2710,"aliases":2757,"links":2762,"related":2765,"readMore":-1,"target":2766,"hasArticle":2705},"ci-cd","CI\u002FCD","Automatisering die elke wijziging bouwt, test en uitlevert zonder dat iemand handmatig commando's uitvoert. Op mobiel is het wat van een release een druk op de knop maakt in plaats van een middag wachten tot iemand anders tijd heeft.",[2758,2759,2760,2761],"CI","continuous integration","continuous delivery","continue integratie",[2763],{"kind":2720,"url":2764},"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FCI\u002FCD",[],"\u002Fglossary#ci-cd",{"slug":2768,"term":2769,"definition":2770,"category":2771,"aliases":2772,"links":2774,"related":2777,"readMore":-1,"target":2780,"hasArticle":2705},"crud","CRUD","Create, read, update, delete — de vier bewerkingen achter zo goed als elk formulier en elk beheerscherm. Verzamelnaam voor de helft van een app die routinematig gegevensbeheer is, tegenover de delen die echte domeinlogica dragen.","architecture",[2773],"Create, Read, Update, Delete",[2775],{"kind":2720,"url":2776},"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FCreate,_read,_update_and_delete",[2778,2779],"rest","graphql","\u002Fglossary#crud",{"slug":2782,"term":2783,"definition":2784,"category":2771,"aliases":2785,"links":2791,"related":2794,"readMore":-1,"target":2797,"hasArticle":2705},"container-registry","Containerregistry","Een gehoste opslagplaats voor container-images, aangesproken op naam en tag — Docker Hub, GitHub Container Registry, of die van je eigen cloudprovider. Een build daarheen pushen verandert 'werkt op mijn machine' in een image die iedereen ongewijzigd kan pullen en draaien.",[2786,2787,2788,2789,2790],"container registry","image registry","Docker registry","GHCR","ghcr.io",[2792],{"kind":2733,"url":2793},"https:\u002F\u002Fdocs.github.com\u002Fen\u002Fpackages\u002Fworking-with-a-github-packages-registry\u002Fworking-with-the-container-registry",[2795,2796],"dev-container","multi-arch-image","\u002Fglossary#container-registry",{"slug":2736,"term":2799,"definition":2800,"category":2696,"aliases":2801,"links":2806,"related":2809,"readMore":2810,"target":2810,"hasArticle":800},"Deep linking","Een link die een specifiek scherm in een geïnstalleerde app opent in plaats van het beginscherm of een webpagina. Deferred deep linking overleeft een installatie, zodat een tik die via de appwinkel loopt alsnog op het juiste scherm landt.",[2802,2803,2804,2805],"deferred deep linking","universal links","Android App Links","diepe links",[2807],{"kind":2720,"url":2808},"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FMobile_deep_linking",[2727,2737],"\u002Fglossary\u002Fdeep-linking",{"slug":2795,"term":2812,"definition":2813,"category":2710,"aliases":2814,"links":2819,"related":2824,"readMore":-1,"target":2825,"hasArticle":2705},"Dev Container","Een ontwikkelomgeving die één keer beschreven staat in devcontainer.json — het OS, de tools en de runtimeversies die een project nodig heeft — en die bij iedere bijdrager identiek in een container opent, in plaats van een installatiehandleiding die iedereen anders leest.",[2815,2816,2817,2818],"devcontainer.json","Dev Containers","VS Code Dev Containers","ontwikkelcontainer",[2820,2822],{"kind":2701,"url":2821},"https:\u002F\u002Fcontainers.dev\u002F",{"kind":2733,"url":2823},"https:\u002F\u002Fcode.visualstudio.com\u002Fdocs\u002Fdevcontainers\u002Fcontainers",[2782],"\u002Fglossary#dev-container",{"slug":2827,"term":2828,"definition":2829,"category":2771,"aliases":2830,"links":2835,"related":2838,"readMore":-1,"target":2839,"hasArticle":2705},"floating-point","Drijvende komma","Het binaire IEEE 754-formaat achter double en float. Het kan 0,1 niet exact weergeven, dus 0.1 + 0.2 is 0.30000000000000004 — onzichtbaar in graphics en fataal bij geld, dat thuishoort in gehele kleinste eenheden (centen) of een decimaal type.",[2831,2832,2833,2834],"IEEE 754","double","floating point","zwevende komma",[2836],{"kind":2720,"url":2837},"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FIEEE_754",[],"\u002Fglossary#floating-point",{"slug":2751,"term":2841,"definition":2842,"category":2696,"aliases":2843,"links":2846,"related":2851,"readMore":-1,"target":2852,"hasArticle":2705},"Elasticsearch","Een zoek- en analysemachine die records indexeert zodat je ze interactief kunt filteren en doorzoeken in plaats van ze te scannen. Waar je naar grijpt als de vraag 'laat me deze specifieke sessies zien, op zes manieren ingeperkt' is en niet 'tel deze kolom op'.",[2844,2845],"Elastic","ELK",[2847,2849],{"kind":2701,"url":2848},"https:\u002F\u002Fwww.elastic.co\u002Felasticsearch",{"kind":2720,"url":2850},"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FElasticsearch",[2740],"\u002Fglossary#elasticsearch",{"slug":2854,"term":2855,"definition":2856,"category":2771,"aliases":2857,"links":2861,"related":2864,"readMore":-1,"target":2867,"hasArticle":2705},"end-to-end-encryption","End-to-end-versleuteling","Versleuteling die op het verzendende apparaat wordt aangebracht en pas op het ontvangende weer wordt weggehaald, zodat de dienst die het bericht vervoert het niet kan lezen — niet na een vordering, niet na een datalek. Het beschermt de inhoud, nooit de metadata.",[2858,2859,2860],"E2EE","end-to-end encryption","end-to-end versleuteld",[2862],{"kind":2720,"url":2863},"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FEnd-to-end_encryption",[2865,2866],"tls","jwt","\u002Fglossary#end-to-end-encryption",{"slug":2869,"term":2870,"definition":2871,"category":2771,"aliases":2872,"links":2876,"related":2877,"readMore":-1,"target":2881,"hasArticle":2705},"fan-out","Fan-out","Eén keer uit een bron lezen en elke update afleveren bij iedere client die erop geabonneerd is. De naïeve versie schrijft in een lus naar abonnees en loopt vast zodra één socket traag is; een echte buffert per client en laat vallen wie niet kan bijbenen.",[2873,2874,2875],"fanout","broadcast","uitzending",[],[2878,2879,2880],"websocket","pub-sub","server-sent-events","\u002Fglossary#fan-out",{"slug":2883,"term":2884,"definition":2885,"category":2710,"aliases":2886,"links":2890,"related":2893,"readMore":-1,"target":2897,"hasArticle":2705},"feature-flags","Feature flags","Schakelaars die functionaliteit aan- of uitzetten vanuit configuratie in plaats van vanuit een release. Ze laten één binary zich per merk, markt of gebruiker anders gedragen, en laten je een riskante functie uitzetten zonder een nieuwe build door de review te sturen.",[2887,2888,2889],"feature flag","feature toggle","functieschakelaar",[2891],{"kind":2720,"url":2892},"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FFeature_toggle",[2894,2895,2896],"staged-rollout","white-label","multi-tenancy","\u002Fglossary#feature-flags",{"slug":2894,"term":2899,"definition":2900,"category":2710,"aliases":2901,"links":2905,"related":2906,"readMore":-1,"target":2907,"hasArticle":2705},"Gefaseerde uitrol","Een build eerst naar een klein percentage gebruikers uitbrengen en pas verbreden als het crashvrije percentage standhoudt. Een slechte build die je bij tien procent betrapt is een slechte middag; dezelfde build bij honderd procent is een slechte week.",[2902,2903,2904],"staged rollout","gefaseerde release","canary release",[],[2883,2754],"\u002Fglossary#staged-rollout",{"slug":2909,"term":2910,"definition":2911,"category":2710,"aliases":2912,"links":2916,"related":2919,"readMore":-1,"target":2920,"hasArticle":2705},"golden-test","Golden test","Een test die een widget rendert en het resultaat pixel voor pixel vergelijkt met een opgeslagen referentiebeeld. In Flutter is het de goedkoopste manier om te zien of een herontwerp de empty state brak op 320pt, in donkere modus, bij 200% tekstgrootte.",[2913,2914,2915],"golden tests","screenshottest","snapshottest",[2917],{"kind":2733,"url":2918},"https:\u002F\u002Fapi.flutter.dev\u002Fflutter\u002Fflutter_test\u002FmatchesGoldenFile.html",[2754],"\u002Fglossary#golden-test",{"slug":2779,"term":2922,"definition":2923,"category":2771,"aliases":2924,"links":2925,"related":2930,"readMore":-1,"target":2931,"hasArticle":2705},"GraphQL","Een querytaal voor API's waarin de client precies de velden benoemt die hij wil en één antwoord terugkrijgt dat daarop past. Het haalt de over-fetching weg waar REST-endpoints naar afglijden, en voegt er een eigen faalmodus aan toe: een onbegrensde query die het hele datamodel afloopt.",[],[2926,2928],{"kind":2701,"url":2927},"https:\u002F\u002Fgraphql.org\u002F",{"kind":2720,"url":2929},"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FGraphQL",[2778],"\u002Fglossary#graphql",{"slug":2724,"term":2933,"definition":2934,"category":2710,"aliases":2935,"links":2937,"related":2940,"readMore":-1,"target":2941,"hasArticle":2705},"HIPAA","De Amerikaanse wet over beschermde gezondheidsinformatie — hoe die opgeslagen, verstuurd, gelogd en gedeeld mag worden. Net als PCI-DSS is het een architectuurkeuze aan het begin, geen beleidsstuk dat je vlak voor de lancering toevoegt.",[2936],"Health Insurance Portability and Accountability Act",[2938],{"kind":2720,"url":2939},"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FHealth_Insurance_Portability_and_Accountability_Act",[2594,2707,2723],"\u002Fglossary#hipaa",{"slug":2943,"term":2944,"definition":2945,"category":2771,"aliases":2946,"links":2949,"related":2952,"readMore":-1,"target":2954,"hasArticle":2705},"headless-cms","Headless CMS","Een contentsysteem met een redactieomgeving en een API, maar zonder eigen front-end. Redacteuren publiceren op één plek, en de site of app rendert die content zelf — zodat de presentatielaag van jou is en niet van de CMS-leverancier.",[2947,2948],"headless contentmanagementsysteem","content-API",[2950],{"kind":2720,"url":2951},"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FHeadless_content_management_system",[2953,2778],"server-side-rendering","\u002Fglossary#headless-cms",{"slug":2600,"term":2391,"definition":2956,"category":2696,"aliases":2957,"links":2960,"related":2963,"readMore":-1,"target":2964,"hasArticle":2705},"Een Android-telefoon zich in software laten gedragen als een contactloze kaart via NFC, zonder secure element in hardware. Zo betaalt een wallet-app bij een terminal: de telefoon spreekt hetzelfde contactloze EMV-protocol als de plastic kaart zou hebben gedaan.",[2958,2393,2959],"HCE","contactloos betalen",[2961],{"kind":2733,"url":2962},"https:\u002F\u002Fdeveloper.android.com\u002Fdevelop\u002Fconnectivity\u002Fnfc\u002Fhce",[2594,2612],"\u002Fglossary#host-card-emulation",{"slug":2966,"term":2967,"definition":2968,"category":2696,"aliases":2969,"links":2970,"related":2973,"readMore":-1,"target":2975,"hasArticle":2705},"impeller","Impeller","De renderengine die Flutter vandaag gebruikt, standaard op iOS sinds 2023 en op Android sinds 2024. Hij compileert zijn shaders vooraf in plaats van tijdens de eerste animatie, wat de shader-jank wegnam die Flutters zichtbaarste productieprobleem was.",[],[2971],{"kind":2733,"url":2972},"https:\u002F\u002Fdocs.flutter.dev\u002Fperf\u002Fimpeller",[2974],"skia","\u002Fglossary#impeller",{"slug":2977,"term":2978,"definition":2979,"category":2980,"aliases":2981,"links":2985,"related":2988,"readMore":-1,"target":2990,"hasArticle":2705},"in-app-purchase","In-app-aankoop","Digitale goederen of een abonnement verkopen via de Apple- of Google-billing die beide winkels verplicht stellen voor digitale content en waarover ze commissie rekenen. Het moeilijke is nooit de aankoop; het is het herstellen ervan op een nieuw toestel en de rechten kloppend houden.","business",[2982,2983,2984],"IAP","in-app purchase","in-app-abonnement",[2986],{"kind":2733,"url":2987},"https:\u002F\u002Fdeveloper.apple.com\u002Fin-app-purchase\u002F",[2989],"product-market-fit","\u002Fglossary#in-app-purchase",{"slug":2737,"term":2992,"definition":2993,"category":2696,"aliases":2994,"links":2995,"related":2998,"readMore":-1,"target":2999,"hasArticle":2705},"Install Referrer","Een Google Play-API die een net geïnstalleerde Android-app de campagneparameters overhandigt van de link die tot de installatie leidde. De Android-helft van deferred deep linking, en de betrouwbare manier om te weten waar een gebruiker vandaan kwam.",[],[2996],{"kind":2733,"url":2997},"https:\u002F\u002Fdeveloper.android.com\u002Fgoogle\u002Fplay\u002Finstallreferrer",[2736,2727],"\u002Fglossary#install-referrer",{"slug":2866,"term":3001,"definition":3002,"category":3003,"aliases":3004,"links":3006,"related":3011,"readMore":-1,"target":3012,"hasArticle":2705},"JWT","Een ondertekend token dat zijn eigen claims meedraagt, zodat een server kan zien bij wie een verzoek hoort zonder een sessie op te zoeken. Standaard voor mobiele authenticatie. De handtekening bewijst dat er niets is gewijzigd; ze verbergt niet wat erin staat.","protocol",[3005],"JSON Web Token",[3007,3009],{"kind":2701,"url":3008},"https:\u002F\u002Fjwt.io\u002F",{"kind":2720,"url":3010},"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FJSON_Web_Token",[2594],"\u002Fglossary#jwt",{"slug":3014,"term":3015,"definition":3016,"category":2710,"aliases":3017,"links":3022,"related":3025,"readMore":-1,"target":3026,"hasArticle":2705},"kyc","KYC","Know Your Customer — de identiteitscontroles die een gereguleerd financieel product uitvoert voordat het iemand geld laat verplaatsen: documentscans, liveness, sanctie- en witwascontroles. Het bepaalt de onboarding sterker dan welke ontwerpkeuze ook.",[3018,3019,3020,3021],"Know Your Customer","AML","KYC\u002FAML","witwascontrole",[3023],{"kind":2720,"url":3024},"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FKnow_your_customer",[2594,2707],"\u002Fglossary#kyc",{"slug":76,"term":74,"definition":3028,"category":2696,"aliases":3029,"links":3031,"related":3032,"readMore":3033,"target":3033,"hasArticle":2705},"Bedrijfslogica geschreven in Kotlin delen tussen Android, iOS en de server, terwijl elk platform zijn eigen native UI houdt. Het alternatief voor Flutter wanneer de interface native moet zijn maar de regels erachter niet.",[3030],"KMP",[],[],"\u002Ftechnologies\u002Fkmp",{"slug":3035,"term":3036,"definition":3037,"category":2980,"aliases":3038,"links":3041,"related":3044,"readMore":-1,"target":3045,"hasArticle":2705},"mvp","MVP","De kleinste versie van een product die je aan echte gebruikers kunt uitbrengen en die nog steeds de vraag beantwoordt waarvoor je het bouwde. Een besluit over scope, niet over kwaliteit — een MVP moet nog altijd werken.",[3039,3040],"minimum viable product","minimaal levensvatbaar product",[3042],{"kind":2720,"url":3043},"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FMinimum_viable_product",[2895],"\u002Fglossary#mvp",{"slug":2796,"term":3047,"definition":3048,"category":2771,"aliases":3049,"links":3054,"related":3057,"readMore":-1,"target":3058,"hasArticle":2705},"Multi-architectuur-image","Eén image-tag die per CPU-architectuur naar andere binaries verwijst — linux\u002Famd64 en linux\u002Farm64 zijn het gebruikelijke paar — zodat dezelfde docker pull ongewijzigd werkt op Intel- en AMD-servers en op Apple Silicon-laptops.",[3050,3051,3052,3053],"multi-arch build","multi-arch image","multi-platform image","linux\u002Famd64 + linux\u002Farm64",[3055],{"kind":2733,"url":3056},"https:\u002F\u002Fdocs.docker.com\u002Fbuild\u002Fbuilding\u002Fmulti-platform\u002F",[2782],"\u002Fglossary#multi-arch-image",{"slug":2896,"term":3060,"definition":3061,"category":2771,"aliases":3062,"links":3065,"related":3068,"readMore":-1,"target":3069,"hasArticle":2705},"Multi-tenancy","Eén deployment die veel klanten bedient, waarbij elke klant alleen zijn eigen data, configuratie en ingeschakelde functies ziet doordat de tenantcontext per verzoek wordt bepaald. Het is wat een vloot merkapps één product maakt in plaats van veel forks.",[3063,3064],"multi-tenant","tenant",[3066],{"kind":2720,"url":3067},"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FMultitenancy",[2895,2883],"\u002Fglossary#multi-tenancy",{"slug":3071,"term":3072,"definition":3073,"category":3003,"aliases":3074,"links":3078,"related":3083,"readMore":-1,"target":3084,"hasArticle":2705},"oauth","OAuth","De standaard achter Inloggen met Apple, Google en de rest: de gebruiker geeft je app toestemming bij een aanbieder die hij al vertrouwt, en je app krijgt een token in plaats van zijn wachtwoord. Niemand hoeft nieuwe inloggegevens te verzinnen en jij bewaart ze nergens.",[3075,3076,3077],"OAuth 2.0","social login","Inloggen met Apple",[3079,3081],{"kind":2701,"url":3080},"https:\u002F\u002Foauth.net\u002F2\u002F",{"kind":2720,"url":3082},"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FOAuth",[2866],"\u002Fglossary#oauth",{"slug":2750,"term":3086,"definition":3087,"category":2771,"aliases":3088,"links":3093,"related":3096,"readMore":-1,"target":3097,"hasArticle":2705},"Objectopslag","Opslag die een heel bestand onder een sleutel bewaart in plaats van in een mappenboom — Amazon S3 en de vele diensten die zijn API spreken. Goedkoop, praktisch onbeperkt, en de gebruikelijke plek voor ruwe events, back-ups en media: eenmaal geschreven, zelden gelezen, eeuwig bewaard.",[3089,3090,3091,3092],"S3","S3-compatibele opslag","object storage","blob storage",[3094],{"kind":2720,"url":3095},"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FObject_storage",[2740],"\u002Fglossary#object-storage",{"slug":2594,"term":3099,"definition":3100,"category":2710,"aliases":3101,"links":3104,"related":3109,"readMore":-1,"target":3110,"hasArticle":2705},"PCI-DSS","De beveiligingsstandaard van de kaartindustrie die iedereen bindt die kaartgegevens opslaat, verwerkt of verstuurt. De meeste apps blijven er bewust buiten door de kaartinvoer over te laten aan een gecertificeerde betaaldienstverlener.",[3102,3103],"PCI DSS","Payment Card Industry Data Security Standard",[3105,3107],{"kind":2701,"url":3106},"https:\u002F\u002Fwww.pcisecuritystandards.org\u002F",{"kind":2720,"url":3108},"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FPayment_Card_Industry_Data_Security_Standard",[2866],"\u002Fglossary#pci-dss",{"slug":3112,"term":3113,"definition":3114,"category":2696,"aliases":3115,"links":3119,"related":3122,"readMore":-1,"target":3123,"hasArticle":2705},"platform-channels","Platform channels","De brug waarmee een Flutter-app native iOS- en Android-code aanroept — Keychain en Keystore, biometrie, betaalschermen, elke SDK zonder Dart-pakket. Routinewerk maar echt werk, en de eerste plek waar een engineer die nooit buiten Dart kwam vastloopt.",[3116,3117,3118],"platform channel","method channel","platformkanaal",[3120],{"kind":2733,"url":3121},"https:\u002F\u002Fdocs.flutter.dev\u002Fplatform-integration\u002Fplatform-channels",[76],"\u002Fglossary#platform-channels",{"slug":3125,"term":3126,"definition":3127,"category":3003,"aliases":3128,"links":3132,"related":3135,"readMore":-1,"target":3136,"hasArticle":2705},"post-quantum-cryptography","Post-quantumcryptografie","Versleutelingsalgoritmen die veilig moeten blijven tegen een toekomstige quantumcomputer, inmiddels gestandaardiseerd door NIST. De migratie is nu al dringend, nog vóór die hardware bestaat, omdat verkeer dat vandaag wordt onderschept later alsnog te ontsleutelen valt.",[3129,3130,3131],"PQC","post-quantum cryptography","postkwantumcryptografie",[3133],{"kind":2720,"url":3134},"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FPost-quantum_cryptography",[2865,2854],"\u002Fglossary#post-quantum-cryptography",{"slug":2989,"term":3138,"definition":3139,"category":2980,"aliases":3140,"links":3144,"related":3147,"readMore":-1,"target":3149,"hasArticle":2705},"Product-market fit","Het punt waarop een product aantoonbaar mensen heeft gevonden die het willen — ze gebruiken het, komen terug en betalen. Daarvoor beantwoordt engineering een vraag; daarna bedient engineering de vraag.",[3141,3142,3143],"PMF","product\u002Fmarket fit","productmarktfit",[3145],{"kind":2720,"url":3146},"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FProduct-market_fit",[3035,3148],"time-to-market","\u002Fglossary#product-market-fit",{"slug":3151,"term":3152,"definition":3153,"category":2710,"aliases":3154,"links":3157,"related":3160,"readMore":-1,"target":3162,"hasArticle":2705},"prompt-injection","Prompt injection","Een aanval waarbij tekst van een gebruiker door een taalmodel als instructie wordt gelezen in plaats van als data, en het model zo om zijn eigen regels heen wordt gestuurd. De LLM-variant van SQL-injectie, en hij duikt overal op waar gebruikersinvoer in een prompt wordt geplakt.",[3155,3156],"injectieaanval","injection attack",[3158],{"kind":2720,"url":3159},"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FPrompt_injection",[3161],"rate-limiting","\u002Fglossary#prompt-injection",{"slug":2879,"term":3164,"definition":3165,"category":2771,"aliases":3166,"links":3169,"related":3172,"readMore":-1,"target":3173,"hasArticle":2705},"Pub\u002FSub","Een berichtenpatroon waarin een producent een gebeurtenis publiceert en willekeurig veel consumenten die elk voor zich lezen, met een broker ertussen. De producent wacht nooit op ze, en zo blijft een verzoekpad snel terwijl het tragere werk erachter gebeurt.",[3167,3168],"publish\u002Fsubscribe","Google Cloud Pub\u002FSub",[3170],{"kind":2720,"url":3171},"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FPublish%E2%80%93subscribe_pattern",[],"\u002Fglossary#pub-sub",{"slug":2778,"term":3175,"definition":3176,"category":2771,"aliases":3177,"links":3180,"related":3183,"readMore":-1,"target":3184,"hasArticle":2705},"REST","De gangbare stijl voor HTTP-API's: een URL benoemt een resource en het HTTP-werkwoord zegt wat ermee moet gebeuren. De standaardmanier waarop een app met een backend praat, en wat de meeste externe integraties verwachten aan te treffen.",[3178,3179],"REST API","Representational State Transfer",[3181],{"kind":2720,"url":3182},"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FREST",[2878,2880],"\u002Fglossary#rest",{"slug":3161,"term":3186,"definition":3187,"category":2710,"aliases":3188,"links":3192,"related":3195,"readMore":-1,"target":3196,"hasArticle":2705},"Rate limiting","Een bovengrens op het aantal verzoeken dat één aanroeper binnen een bepaald venster mag doen. Het houdt tegen dat één enthousiaste gebruiker, een scraper of een bot in een middag een maand aan betaald API-budget opmaakt, en het moet aan jouw kant van de integratie staan.",[3189,3190,3191],"rate limit","throttling","verzoeklimiet",[3193],{"kind":2720,"url":3194},"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FRate_limiting",[3151],"\u002Fglossary#rate-limiting",{"slug":2723,"term":3198,"definition":3199,"category":2710,"aliases":3200,"links":3203,"related":3206,"readMore":-1,"target":3207,"hasArticle":2705},"SOC 2","Een rapport van een externe auditor over hoe een organisatie met klantgegevens omgaat — beveiliging, beschikbaarheid, vertrouwelijkheid — en niet een certificaat dat je koopt. Zakelijke kopers vragen erom, en het beperkt de architectuur lang voordat de audit dat doet.",[3201,3202],"SOC2","System and Organization Controls",[3204],{"kind":2720,"url":3205},"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FSystem_and_Organization_Controls",[2594,2707,2724],"\u002Fglossary#soc-2",{"slug":3209,"term":3210,"definition":3211,"category":2980,"aliases":3212,"links":3215,"related":3218,"readMore":-1,"target":3219,"hasArticle":2705},"saas","SaaS","Software die als doorlopend abonnement op een gehost product wordt verkocht in plaats van als eenmalige licentie die de klant zelf installeert en draait. De leverancier beheert de servers, levert doorlopend updates en factureert per gebruiker of per verbruik.",[3213,3214],"Software as a Service","software-as-a-service",[3216],{"kind":2720,"url":3217},"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FSoftware_as_a_service",[2896,2895],"\u002Fglossary#saas",{"slug":2880,"term":3221,"definition":3222,"category":3003,"aliases":3223,"links":3225,"related":3228,"readMore":-1,"target":3229,"hasArticle":2705},"Server-Sent Events","Een eenrichtingsstroom van server naar client over een gewone HTTP-verbinding. Eenvoudiger dan een WebSocket en het volstaat overal waar alleen de server iets te melden heeft — een voortgangsfeed, een AI-antwoord dat token voor token binnenkomt.",[3224],"SSE",[3226],{"kind":2720,"url":3227},"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FServer-sent_events",[2878,2778],"\u002Fglossary#server-sent-events",{"slug":2953,"term":3231,"definition":3232,"category":2771,"aliases":3233,"links":3237,"related":3240,"readMore":-1,"target":3241,"hasArticle":2705},"Server-side rendering","Een pagina op de server als kant-en-klare HTML opbouwen, zodat het eerste antwoord de inhoud, koppen, metatags en gestructureerde data al meedraagt. Crawlers, linkvoorbeelden en trage apparaten lezen dat zonder JavaScript uit te voeren.",[3234,3235,3236],"SSR","server-rendered","renderen op de server",[3238],{"kind":2720,"url":3239},"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FServer-side_scripting",[2943],"\u002Fglossary#server-side-rendering",{"slug":2974,"term":3243,"definition":3244,"category":2696,"aliases":3245,"links":3248,"related":3253,"readMore":-1,"target":3254,"hasArticle":2705},"Skia","De opensource 2D-graphicsbibliotheek van Google die Chrome, Android en — tot Impeller — elk Flutter-frame tekent. Ze rendert net zo goed naar PDF als naar een scherm, en dat is wat print-naar-PDF in Chrome doet.",[3246,3247],"Skia Graphics Engine","skia-safe",[3249,3251],{"kind":2701,"url":3250},"https:\u002F\u002Fskia.org\u002F",{"kind":2720,"url":3252},"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FSkia_Graphics_Engine",[2966],"\u002Fglossary#skia",{"slug":3256,"term":2202,"definition":3257,"category":2771,"aliases":3258,"links":3262,"related":3265,"readMore":-1,"target":3266,"hasArticle":2705},"state-management","Hoe een app bepaalt waar een waarde leeft, wie hem mag wijzigen en welke delen van het scherm opnieuw tekenen als dat gebeurt. In Flutter is de keuze tussen Riverpod, BLoC en Provider een van de eerste architectuurbesluiten en het lastigst te herzien.",[3259,3260,3261],"statemanagement","BLoC","Riverpod",[3263],{"kind":2733,"url":3264},"https:\u002F\u002Fdocs.flutter.dev\u002Fdata-and-backend\u002Fstate-mgmt\u002Foptions",[2909],"\u002Fglossary#state-management",{"slug":2865,"term":3268,"definition":3269,"category":3003,"aliases":3270,"links":3274,"related":3277,"readMore":-1,"target":3278,"hasArticle":2705},"TLS","De versleutelingslaag onder HTTPS. Ze bewijst dat de server is wie zijn certificaat zegt, spreekt een verse sleutel voor de sessie af en versleutelt alles daarna — zodat het netwerk ertussen alleen cijfertekst ziet die het niet stiekem kan wijzigen.",[3271,3272,3273],"SSL","HTTPS","Transport Layer Security",[3275],{"kind":2720,"url":3276},"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FTransport_Layer_Security",[2854,2866],"\u002Fglossary#tls",{"slug":3280,"term":3281,"definition":3282,"category":2980,"aliases":3283,"links":3289,"related":3290,"readMore":-1,"target":3292,"hasArticle":2705},"staff-augmentation","Teamuitbreiding","Een inhuurmodel waarbij engineers van een externe partner bij je team komen en onder jouw aansturing werken — in jouw repository, jouw sprints, jouw proces — in plaats van een eigen project op te leveren. Je koopt capaciteit; de code en de context blijven bij jou.",[3284,3285,3286,3287,3288],"staff augmentation","team augmentation","dedicated developers","outstaffing","teamversterking",[],[3291,3148],"total-cost-of-ownership","\u002Fglossary#staff-augmentation",{"slug":3148,"term":3294,"definition":3295,"category":2980,"aliases":3296,"links":3299,"related":3302,"readMore":-1,"target":3303,"hasArticle":2705},"Time to market","Hoe lang het duurt om een product van besluit naar echte gebruikers te krijgen. De meeste discussies over stack en scope gaan eigenlijk over dit getal, want elke gewonnen week is een week omzet, feedback en concurrentiepositie.",[3297,3148,3298],"TTM","doorlooptijd tot de markt",[3300],{"kind":2720,"url":3301},"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FTime_to_market",[3035,3291],"\u002Fglossary#time-to-market",{"slug":3291,"term":3305,"definition":3306,"category":2980,"aliases":3307,"links":3311,"related":3314,"readMore":-1,"target":3315,"hasArticle":2705},"Total cost of ownership","Wat een product over zijn hele leven kost in plaats van alleen de bouw: onderhoud, upgrades, jaarlijkse OS- en winkelmigraties, en het tweede team dat je aanneemt om twee codebases gelijk te houden. Meestal groter dan de bouwofferte, en er vrijwel nooit in opgenomen.",[3308,3309,3310],"TCO","kosten van eigenaarschap","totale eigendomskosten",[3312],{"kind":2720,"url":3313},"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FTotal_cost_of_ownership",[3148],"\u002Fglossary#total-cost-of-ownership",{"slug":3317,"term":2210,"definition":3318,"category":3003,"aliases":3319,"links":3321,"related":3326,"readMore":-1,"target":3328,"hasArticle":2705},"webrtc","De standaard voor browsers en mobiel om audio, video en data rechtstreeks tussen twee apparaten te sturen, waarbij servers er alleen aan te pas komen om ze aan elkaar voor te stellen. Hierop is een videogesprek in een app gebouwd, tenzij er een gehuurde SDK onder zit.",[3320],"Web Real-Time Communication",[3322,3324],{"kind":2701,"url":3323},"https:\u002F\u002Fwebrtc.org\u002F",{"kind":2720,"url":3325},"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FWebRTC",[3327,2878],"xmpp","\u002Fglossary#webrtc",{"slug":2878,"term":3330,"definition":3331,"category":3003,"aliases":3332,"links":3333,"related":3336,"readMore":-1,"target":3337,"hasArticle":2705},"WebSocket","Een protocol dat één verbinding openhoudt tussen client en server, zodat beide kanten op elk moment kunnen sturen in plaats van dat de client keer op keer vraagt. Waar live koersen, chat en aanwezigheidsindicatoren op draaien.",[],[3334],{"kind":2720,"url":3335},"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FWebSocket",[2880,2778],"\u002Fglossary#websocket",{"slug":2895,"term":3339,"definition":3340,"category":2980,"aliases":3341,"links":3345,"related":3348,"readMore":3349,"target":3349,"hasArticle":800},"White-label","Eén product dat onder veel merken wordt uitgebracht. Een white-label platform voor mobiel bouwt elke klant een winkelklare app met een eigen naam, ontwerp en content vanuit één gedeelde codebase, in plaats van het project per klant te forken.",[3342,3343,3344],"white label","multi-tenant app","merkloos product",[3346],{"kind":2720,"url":3347},"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FWhite-label_product",[3035],"\u002Fglossary\u002Fwhite-label",{"slug":3327,"term":3351,"definition":3352,"category":3003,"aliases":3353,"links":3356,"related":3361,"readMore":-1,"target":3362,"hasArticle":2705},"XMPP","Een open, gefedereerd berichtenprotocol, en al lang het alternatief voor zelf een chatbackend schrijven of er een huren. Het dekt ook aanwezigheid, typindicatoren en bestandsoverdracht, en het is oud genoeg dat elk platform een volwassen clientbibliotheek heeft.",[3354,3355],"Jabber","Extensible Messaging and Presence Protocol",[3357,3359],{"kind":2701,"url":3358},"https:\u002F\u002Fxmpp.org\u002F",{"kind":2720,"url":3360},"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FXMPP",[3317,2878],"\u002Fglossary#xmpp"]