[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"technologies":3,"\u002Fblog\u002Fimpossible-states-dart-sealed-classes":167,"team-members":2189,"blog-topics":2531,"mdc--12ftuw-key":2572,"glossary":2692},[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","Every app we ship reaches Android — platform APIs, background execution, permissions, and the store requirements that come with them.","android","framework","logos:android-icon",{"title":11,"description":12,"slug":13,"category":8,"icon":14},"Angular","The front-end framework we work in when a product is already Angular — a batteries-included structure that suits large, long-lived apps.","angular","logos:angular-icon",{"title":16,"description":17,"slug":18,"category":19,"icon":20},"Dart","The language every Flutter app we ship is written in — sound null safety, real pattern matching, and a compiler that targets native and the web.","dart","language","logos:dart",{"title":22,"description":23,"slug":24,"category":8,"icon":25},"Django","The Python framework we use when a product needs an admin, auth and a real data model on day one rather than a hand-rolled service.","django","logos:django-icon",{"title":27,"description":28,"slug":29,"category":30,"icon":31},"Docker","Every service we build ships as a container, so what runs on a laptop, in CI and in production is one artefact rather than three of them.","docker","infrastructure","logos:docker-icon",{"title":33,"description":34,"slug":35,"category":36,"icon":37},"Fastlane","The release automation behind our mobile work — signing, builds and store uploads run from CI instead of from one engineer's laptop on release day.","fastlane","tools","logos:fastlane",{"title":39,"description":40,"slug":41,"category":8,"icon":42},"Flutter","Our primary mobile stack since 2018 — one Dart codebase shipping to iOS, Android, web and desktop without a separate team per platform.","flutter","logos:flutter",{"title":44,"description":45,"slug":46,"category":36,"icon":47},"Git","Every project we touch lives in Git — reviewed pull requests, CI on every branch, and a history that still makes sense months later.","git","logos:git-icon",{"title":49,"description":50,"slug":51,"category":19,"icon":52},"Go","Our backend language for real-time APIs and services under load — small binaries, fast builds, and concurrency that stays readable.","go","logos:gopher",{"title":54,"description":55,"slug":56,"category":30,"icon":57},"Google Cloud","The cloud our production workloads run on — managed Kubernetes, storage and networking, without hand-built servers nobody wants to maintain.","gcp","logos:google-cloud",{"title":59,"description":60,"slug":61,"category":36,"icon":62},"Gradle","The build system every Android release goes through — product flavours, signing configs, and the dependency wiring under a Flutter app.","gradle","logos:gradle",{"title":64,"description":65,"slug":66,"category":30,"icon":67},"Helm","How we package a Kubernetes deployment — service, config, secrets and ingress as one versioned unit that can be promoted and rolled back.","helm","logos:helm",{"title":69,"description":70,"slug":71,"category":19,"icon":72},"Kotlin","What we reach for when a Flutter app needs real Android underneath it — platform channels, background work, and native SDK integrations.","kotlin","logos:kotlin-icon",{"title":74,"description":75,"slug":76,"category":8,"icon":72},"Kotlin Multiplatform","Sharing business logic across iOS and Android while each platform keeps its own native UI — the alternative when Flutter is not the right fit.","kmp",{"title":78,"description":79,"slug":80,"category":30,"icon":81},"Kubernetes","How we run services in production — Helm-packaged deployments, rollouts that can be rolled back, and scaling that does not need a person at 3am.","kubernetes","logos:kubernetes",{"title":83,"description":84,"slug":85,"category":30,"icon":86},"NATS","Lightweight messaging between services — publish\u002Fsubscribe and request\u002Freply without the operational weight of a full broker cluster.","nats","logos:nats-icon",{"title":88,"description":89,"slug":90,"category":8,"icon":91},"Nuxt","Vue with server rendering, routing and SEO handled — how we build marketing sites and web apps that must be fast and indexable on first load.","nuxt","logos:nuxt-icon",{"title":93,"description":94,"slug":95,"category":19,"icon":96},"PHP","Where we work with an existing PHP backend — extending it, integrating with it, and building the mobile and web clients it has to serve.","php","logos:php",{"title":98,"description":99,"slug":100,"category":101,"icon":102},"PostgreSQL","Our default database — the one we reach for unless a product gives us a specific reason not to, from schema design through to index tuning.","postgres","database","logos:postgresql",{"title":104,"description":105,"slug":106,"category":19,"icon":107},"Python","Our language for backends, data work and AI integrations — including the Python bindings we ship for our own Rust tooling.","python","logos:python",{"title":109,"description":110,"slug":111,"category":8,"icon":112},"React","The front-end library we work in when a product is already React — components, hooks, and the ecosystem that has grown around them.","react","logos:react",{"title":114,"description":115,"slug":116,"category":101,"icon":117},"Redis","Where we put data that has to be fast and can be rebuilt — caches, sessions, rate limits, and the queues behind a product's slow paths.","redis","logos:redis",{"title":119,"description":120,"slug":121,"category":19,"icon":122},"Ruby","The language our mobile release automation is written in — Fastlane lanes, custom actions, and the CI glue that ships builds to the stores.","ruby","logos:ruby",{"title":124,"description":125,"slug":126,"category":19,"icon":127},"Rust","Where we go when performance and correctness both matter — document rendering, CLI tooling, and services that have to stay fast and predictable.","rust","simple-icons:rust",{"title":129,"description":130,"slug":131,"category":101,"icon":132},"SQLite","The database that ships inside the app — local caches, offline-first storage, and anything that still has to work with no network.","sqlite","logos:sqlite",{"title":134,"description":135,"slug":136,"category":8,"icon":137},"Strapi","A headless CMS we reach for when editors need to own the content — a real admin and a clean API, without building either from scratch.","strapi","logos:strapi-icon",{"title":139,"description":140,"slug":141,"category":19,"icon":142},"Swift","What we reach for when a Flutter app needs real iOS underneath it — platform channels, native SDK integrations, widgets and App Clips.","swift","logos:swift",{"title":144,"description":145,"slug":146,"category":8,"icon":142},"SwiftUI","Apple's UI framework, where we build the native surfaces a Flutter app cannot own — widgets, App Clips, extensions and system integrations.","swiftui",{"title":148,"description":149,"slug":150,"category":8,"icon":151},"Tailwind CSS","How we style every front end we build — utility classes and design tokens instead of a stylesheet that only ever grows and is never deleted.","tailwind","logos:tailwindcss-icon",{"title":153,"description":154,"slug":155,"category":19,"icon":156},"TypeScript","The default for everything we write for the browser and for Node services — types that catch integration bugs before they reach a review.","typescript","logos:typescript-icon",{"title":158,"description":159,"slug":160,"category":8,"icon":161},"Vue","Our front-end framework for admin panels, merchant dashboards and product web apps that have to stay maintainable for years, not sprints.","vue","logos:vue",{"title":163,"description":164,"slug":165,"category":36,"icon":166},"Whisper","OpenAI's open-source speech recognition model — the transcription engine behind voice input, running as both a backend service and on-device in the app.","whisper","simple-icons:openai",{"id":168,"title":169,"author":170,"body":171,"description":177,"extension":2172,"hero":2173,"meta":2176,"metaDescription":2177,"metaTitle":2178,"navigation":804,"ogDescription":2179,"path":2180,"seo":2181,"slug":2182,"stem":2183,"summary":2184,"timestamp":2185,"topic":2186,"updated":2187,"__hash__":2188},"blog_en\u002Fblog\u002Fimpossible-states-dart-sealed-classes.md","Five States That Can't Exist, and Your Code Allows All of Them","nixan",{"type":172,"value":173,"toc":2151},"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,726,729,745,759,765,769,775,861,872,891,905,920,927,931,934,940,945,1131,1151,1158,1172,1177,1293,1310,1314,1321,1338,1342,1345,1350,1459,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,2045,2064,2071,2075,2082,2088,2092,2095,2098,2101,2104,2108,2112,2128,2131,2147],[175,176,177],"p",{},"Here is a task you have written twenty versions of: a product list screen. Load from the server, handle the error, render the list.",[175,179,180],{},"We handed it to an agent — Claude Opus 5 — and this came back.",[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],{},"That looks like something you would write yourself. Which is exactly the point: these models were trained on what all of us have been writing by hand for years.",[175,274,275,276,280],{},"Now count. Three fields: a bool, a nullable list, a nullable error. Each has two states you would care about. Two times two times two — ",[277,278,279],"strong",{},"eight combinations."," How many of them mean anything?",[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],{},"What it means",[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],{},"Loading",[288,333,334,339,343,346],{},[315,335,336],{},[188,337,338],{},"false",[315,340,341],{},[188,342,324],{},[315,344,345],{},"set",[315,347,348],{},"Failed",[288,350,351,355,357,361],{},[315,352,353],{},[188,354,338],{},[315,356,345],{},[315,358,359],{},[188,360,324],{},[315,362,363],{},"Loaded",[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],{},"Three. The other five are not compile errors. They are valid code. It builds, it passes review, it ships, and it sits there. ",[188,436,295],{}," is true and there is an error next to it — what do you render? Or everything is empty: not loading, no list, no error. Have we not started yet, or did we go and come back with nothing? The code does not know. It guesses, or the next person to open the file does.",[439,440,442],"h2",{"id":441},"key-takeaways","Key takeaways",[444,445,446,453,459,469,478,488,494,504],"ul",{},[447,448,449,452],"li",{},[277,450,451],{},"An impossible state is one your type permits and your domain forbids."," It is the same thing as a broken invariant, seen from the other side.",[447,454,455,458],{},[277,456,457],{},"The cost is not runtime bugs."," It is a convention nobody wrote down, branches nobody ever considered, and a tax you pay at every read rather than once per incident.",[447,460,461,464,465,468],{},[277,462,463],{},"The invariant always exists."," The only question is whether a person is holding it — a comment, a wiki page, an ",[188,466,467],{},"assert"," that does not run in release — or whether breaking it does not compile.",[447,470,471,477],{},[277,472,473,476],{},[188,474,475],{},"enum Status"," plus nullable fields makes it worse",", not better: twelve combinations instead of eight, and still three that mean anything.",[447,479,480,483,484,487],{},[277,481,482],{},"A sealed class with non-nullable fields leaves exactly the meaningful states"," and makes the rest unbuildable. No booleans, no nullables, no ",[188,485,486],{},"!",".",[447,489,490,493],{},[277,491,492],{},"Exhaustiveness is the payoff."," Add a fourth state and the compiler lists every place that does not handle it — which is feedback an agent can act on, not a wall it hits.",[447,495,496,503],{},[277,497,498,499,502],{},"One ",[188,500,501],{},"_ =>"," branch switches all of that off, silently."," That is the single review rule worth taking from this article.",[447,505,506,509],{},[277,507,508],{},"Algebraic data types do not fix everything."," They eliminate impossible combinations of states, not impossible values, and they do nothing at a system boundary.",[439,511,513],{"id":512},"impossible-states-and-the-older-name-for-them","Impossible states, and the older name for them",[175,515,516],{},"Vocabulary first, because two words get used for one thing.",[175,518,519,521],{},[277,520,451],{}," A screen cannot be loading and showing an error at the same time. The type allows it. That is a hole.",[175,523,524,525,528,529,533],{},"The older and more precise name for it is ",[277,526,527],{},"invariant"," — a rule that has to hold at all times. ",[530,531,532],"em",{},"If we are loading, there is no data yet. If there is an error, it has a message."," An impossible state is just a broken invariant. Same fact from two sides: the invariant is how it is supposed to be, the impossible state is what you got when it was not.",[175,535,536],{},"Hold on to that word. It is where this turns.",[538,539,541],"h3",{"id":540},"why-this-is-a-problem-and-why-the-usual-explanation-is-wrong","Why this is a problem, and why the usual explanation is wrong",[175,543,544],{},"The usual line is that impossible states are bugs that do not crash — they quietly render the wrong screen. That sounds convincing, and if it were true the answer would be cheap: tests, QA, an alert on the empty screen. Catch it, fix it, move on.",[175,546,547,548,551],{},"That is not the problem. It is not about runtime at all. There are three costs, and all three are in your code ",[277,549,550],{},"right now",", even while the app behaves perfectly.",[175,553,554,557,558,560,561,563,564,566,567,569,570,573],{},[277,555,556],{},"One: the convention nobody wrote down."," Look at the agent's class again. Where does it say that when ",[188,559,295],{}," is true you should not read ",[188,562,300],{},"? Nowhere. Where does it say ",[188,565,305],{}," and ",[188,568,300],{}," are never both populated? Nowhere. Those rules exist and people rely on them, but they are not in the type and not in a comment. They live in the head of whoever wrote the class, and everyone after that reconstructs them from the code, reading the ",[188,571,572],{},"if","s and inferring intent. Five minutes a file, for every person who opens it.",[175,575,576,579,580,583],{},[277,577,578],{},"Two: the number of branches."," Eight combinations means eight cases, each of which somebody has to either handle or deliberately rule out. Three get handled. The other five are neither handled nor ruled out — they were ",[277,581,582],{},"never considered."," That distinction matters: a ruled-out case is a decision, an unconsidered one is a hole.",[175,585,586],{},"And the count grows by multiplication.",[282,588,589,602],{},[285,590,591],{},[288,592,593,596,599],{},[291,594,595],{},"Fields that travel together",[291,597,598],{},"Combinations",[291,600,601],{},"Meaningful",[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],{},"Three: you pay at every read, not once per bug."," This is the important one. A bug gets fixed once. A convention that is not in the type gets re-derived by everyone who touches that screen — at review, when adding a feature, while digging through an incident. That is not a one-time cost, it is a tax.",[175,652,653,654,656],{},"Which is also where the usual patch comes from. People plug these holes with conditions: check we are not loading here, check the error is not empty there. Every one of those ",[188,655,572],{},"s is a patch over a hole you cut yourself when you declared three independent fields. Five holes, usually two patches — the ones that already fired.",[175,658,659,660,663],{},"And note, in passing: none of this ",[277,661,662],{},"crashes."," No crash report, no stack trace, nothing in your error tracker. So \"monitoring will catch it\" does not apply. But that is a side effect, not the point.",[175,665,666,667,670],{},"What changed over the last couple of years is ",[277,668,669],{},"volume."," This class used to appear once a week, written by a human who spent at least thirty seconds thinking about the fields. Now it appears in twelve seconds and nobody has read it — not the agent, not you. A reviewer is a person looking at three fields, not somebody holding eight combinations in their head.",[538,672,674],{"id":673},"who-is-holding-the-invariant","Who is holding the invariant?",[175,676,677,678],{},"So: the question is never whether you have an invariant. You always do. The question is ",[277,679,680],{},"who is responsible for it.",[175,682,683,684,686],{},"Usually a person. A comment above the class. A wiki page nobody has opened since the year before last. A code review convention that two people out of five remember. At best an ",[188,685,467],{}," in the constructor.",[688,689,692],"info-box",{":leading-icon":690,":title":691},"lucide:shield-off","An assert is not an invariant in Dart",[175,693,694,695,697],{},"Dart ",[188,696,467],{}," statements are stripped in release builds. Not \"skipped\" — the condition is never evaluated, and neither are its arguments, which is why an expensive check inside an assert costs nothing in production. The practical consequence is the part people forget: an invariant you defended with an assert does not exist where your users are. It held during development, where you were already watching, and it is absent in the one place you are not. Asserts are a debugging aid. They are not a constraint.",[175,699,700,701,704],{},"All of that is an invariant somebody is ",[277,702,703],{},"holding"," — by hand, by attention, by memory. The alternative is to arrange things so there is nothing to hold, because breaking it is not expressible in the type. Not \"we check that this cannot happen\" but \"this does not compile.\"",[175,706,707,708],{},"And here is why that stopped being an academic distinction. An invariant held by a human works exactly as long as code is written at human speed. An agent has not read your comment, has not opened your wiki, was not in your code review. ",[277,709,710],{},"Conventions do not scale to generation speed. Compilers do.",[439,712,714],{"id":713},"algebraic-data-types-in-one-pass","Algebraic data types, in one pass",[175,716,717,718,721,722,725],{},"One disclaimer before the theory, to kill half the objections. The example here is deliberately the most obvious one possible. Loading, error, success is the single most over-used illustration of algebraic data types on the internet — it is in every tutorial. The idea has a famous slogan, ",[530,719,720],{},"make illegal states unrepresentable",", usually credited to Yaron Minsky's ",[530,723,724],{},"Effective ML"," talks, and it has been around for well over a decade. Nothing here is a discovery. The interesting part is where that class at the top of this article came from, and what you do when code like it arrives in batches.",[175,727,728],{},"Now the definition. No category theory.",[175,730,731,734,735,738,739,741,742],{},[277,732,733],{},"A product is \"and\"."," An ordinary class with fields: a string ",[277,736,737],{},"and"," a number ",[277,740,737],{}," a bool. The number of values it can hold is the product of the counts. That is where the eight came from — it is not rhetoric, it is arithmetic. Every field you add ",[277,743,744],{},"multiplies.",[175,746,747,750,751,754,755,758],{},[277,748,749],{},"A sum is \"or\"."," The value is either this or that, and there is no third option. The simplest sum everybody already knows is an ",[188,752,753],{},"enum",". A sealed class is the same sum, except each case can carry its own data. Here the counts ",[277,756,757],{},"add",": loading is one value, failed is however many errors exist, loaded is however many lists exist. Nothing gets multiplied for free.",[175,760,761,762],{},"The whole idea in one sentence: ",[277,763,764],{},"you cannot construct the type in a way that represents a state that was never valid.",[538,766,768],{"id":767},"why-not-just-an-enum","\"Why not just an enum?\"",[175,770,771,772,774],{},"The obvious objection is that you do not need sealed classes for this — add an ",[188,773,475],{}," with three values and stop overthinking it. It is a reasonable instinct, and it does not work.",[182,776,778],{"className":184,"code":777,"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,779,780,800,806,814,825,843,856],{"__ignoreMap":186},[191,781,782,784,787,790,793,795,797],{"class":193,"line":194},[191,783,753],{"class":197},[191,785,786],{"class":201}," Status",[191,788,789],{"class":205}," { loading",[191,791,792],{"class":234},",",[191,794,261],{"class":205},[191,796,792],{"class":234},[191,798,799],{"class":205}," success }\n",[191,801,802],{"class":193,"line":209},[191,803,805],{"emptyLinePlaceholder":804},true,"\n",[191,807,808,810,812],{"class":193,"line":238},[191,809,198],{"class":197},[191,811,202],{"class":201},[191,813,206],{"class":205},[191,815,816,818,820,823],{"class":193,"line":251},[191,817,213],{"class":212},[191,819,786],{"class":201},[191,821,822],{"class":205}," status",[191,824,235],{"class":234},[191,826,827,829,831,833,835,837,839,841],{"class":193,"line":266},[191,828,213],{"class":212},[191,830,216],{"class":201},[191,832,219],{"class":205},[191,834,222],{"class":201},[191,836,225],{"class":205},[191,838,228],{"class":197},[191,840,231],{"class":205},[191,842,235],{"class":234},[191,844,846,848,850,852,854],{"class":193,"line":845},6,[191,847,213],{"class":212},[191,849,256],{"class":201},[191,851,228],{"class":197},[191,853,261],{"class":205},[191,855,235],{"class":234},[191,857,859],{"class":193,"line":858},7,[191,860,269],{"class":205},[175,862,863,864,867,868,871],{},"Count again: three status values, times two list states, times two error states. ",[277,865,866],{},"Twelve combinations."," It was eight. Still three that mean anything. Arithmetically it got ",[277,869,870],{},"worse"," — nine pieces of garbage instead of five.",[175,873,874,875,878,879,566,882,878,884,886,887,890],{},"And the original problem is untouched. ",[188,876,877],{},"status"," is ",[188,880,881],{},"success",[188,883,300],{},[188,885,324],{},": compiles fine. In the UI you are still writing ",[188,888,889],{},"items!",", swearing to the compiler that the data is definitely there. A promise is not a guarantee. It is a disabled check with pleasant syntax.",[175,892,893,894,896,897,900,901,904],{},"The difference is what the sum carries. An ",[188,895,753],{}," is a sum ",[277,898,899],{},"without data"," — it tells you which state you are in, but the data sits alongside it, tied to the state only by your discipline. A sealed class is a sum ",[277,902,903],{},"with data",": the list exists exactly where it means something, and nowhere else.",[175,906,907,908,910,911,913,914],{},"So the rule is: if your states carry no data, use an ",[188,909,753],{},", it is shorter. The moment even one state has a payload, ",[188,912,753],{}," stops helping, and ",[277,915,916,917,919],{},"every ",[188,918,486],{}," in your UI is the receipt.",[175,921,922,923,926],{},"This is an old idea. ML, where it comes from, is fifty years old; Haskell, OCaml and F# have had it for decades. In the mainstream it is now essentially everywhere — Dart got it in version 3, in 2023. What changed is not the idea but the argument for it. ADTs used to be sold on elegance, and aesthetic arguments lose to sprints. The argument now is that ",[277,924,925],{},"code is written faster than it is read",", the only reviewer keeping up with generation is the compiler, and anything you did not express in the type you expressed as a hope.",[439,928,930],{"id":929},"the-rewrite","The rewrite",[175,932,933],{},"Three steps, about two minutes of work.",[175,935,936,939],{},[277,937,938],{},"Step one: write the states down in words, before any code."," The screen is loading. The screen failed. The screen has a list. That is it — three. If you end up with seven at this step, you are almost certainly writing down flag combinations rather than states. A state is something you can name in one word and point at on a mockup.",[175,941,942],{},[277,943,944],{},"Step two: each state becomes its own class.",[182,946,948],{"className":184,"code":947,"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,949,950,962,974,978,982,999,1009,1013,1018,1034,1055,1066,1071,1076,1092,1110,1126],{"__ignoreMap":186},[191,951,952,955,958,960],{"class":193,"line":194},[191,953,954],{"class":197},"sealed",[191,956,957],{"class":197}," class",[191,959,202],{"class":201},[191,961,206],{"class":205},[191,963,964,967,969,972],{"class":193,"line":209},[191,965,966],{"class":212},"  const",[191,968,202],{"class":201},[191,970,971],{"class":205},"()",[191,973,235],{"class":234},[191,975,976],{"class":193,"line":238},[191,977,269],{"class":205},[191,979,980],{"class":193,"line":251},[191,981,805],{"emptyLinePlaceholder":804},[191,983,984,987,989,992,995,997],{"class":193,"line":266},[191,985,986],{"class":212},"final",[191,988,957],{"class":197},[191,990,991],{"class":201}," Loading",[191,993,994],{"class":197}," extends",[191,996,202],{"class":201},[191,998,206],{"class":205},[191,1000,1001,1003,1005,1007],{"class":193,"line":845},[191,1002,966],{"class":212},[191,1004,991],{"class":201},[191,1006,971],{"class":205},[191,1008,235],{"class":234},[191,1010,1011],{"class":193,"line":858},[191,1012,269],{"class":205},[191,1014,1016],{"class":193,"line":1015},8,[191,1017,805],{"emptyLinePlaceholder":804},[191,1019,1021,1023,1025,1028,1030,1032],{"class":193,"line":1020},9,[191,1022,986],{"class":212},[191,1024,957],{"class":197},[191,1026,1027],{"class":201}," Failed",[191,1029,994],{"class":197},[191,1031,202],{"class":201},[191,1033,206],{"class":205},[191,1035,1037,1039,1041,1044,1048,1050,1053],{"class":193,"line":1036},10,[191,1038,966],{"class":212},[191,1040,1027],{"class":201},[191,1042,1043],{"class":205},"(",[191,1045,1047],{"class":1046},"s_hVV","this",[191,1049,487],{"class":234},[191,1051,1052],{"class":205},"error)",[191,1054,235],{"class":234},[191,1056,1058,1060,1062,1064],{"class":193,"line":1057},11,[191,1059,213],{"class":212},[191,1061,256],{"class":201},[191,1063,261],{"class":205},[191,1065,235],{"class":234},[191,1067,1069],{"class":193,"line":1068},12,[191,1070,269],{"class":205},[191,1072,1074],{"class":193,"line":1073},13,[191,1075,805],{"emptyLinePlaceholder":804},[191,1077,1079,1081,1083,1086,1088,1090],{"class":193,"line":1078},14,[191,1080,986],{"class":212},[191,1082,957],{"class":197},[191,1084,1085],{"class":201}," Loaded",[191,1087,994],{"class":197},[191,1089,202],{"class":201},[191,1091,206],{"class":205},[191,1093,1095,1097,1099,1101,1103,1105,1108],{"class":193,"line":1094},15,[191,1096,966],{"class":212},[191,1098,1085],{"class":201},[191,1100,1043],{"class":205},[191,1102,1047],{"class":1046},[191,1104,487],{"class":234},[191,1106,1107],{"class":205},"items)",[191,1109,235],{"class":234},[191,1111,1113,1115,1117,1119,1121,1124],{"class":193,"line":1112},16,[191,1114,213],{"class":212},[191,1116,216],{"class":201},[191,1118,219],{"class":205},[191,1120,222],{"class":201},[191,1122,1123],{"class":205},"> items",[191,1125,235],{"class":234},[191,1127,1129],{"class":193,"line":1128},17,[191,1130,269],{"class":205},[175,1132,1133,1134,1136,1137,1140,1141,1143,1144,1146,1147,1150],{},"Look at what happened. ",[188,1135,348],{}," holds an error, and it is ",[277,1138,1139],{},"not nullable"," — the state \"we failed but there is no error\" no longer exists, and you physically cannot construct it. ",[188,1142,363],{}," holds a list, also non-nullable. ",[188,1145,331],{}," holds ",[277,1148,1149],{},"nothing",", because during loading there is no data, and keeping a field for it is precisely an invitation to an impossible state.",[175,1152,1153,1154,1157],{},"Notice what is not there. ",[277,1155,1156],{},"Not one boolean. Not one nullable field."," That is the entire refactor; the rest is syntax.",[175,1159,1160,1161,1163,1164,1167,1168,1171],{},"One Dart-specific detail: ",[188,1162,954],{}," restricts subtyping to the ",[277,1165,1166],{},"same library",", which in practice means the same file unless you are deliberately splitting it with ",[188,1169,1170],{},"part",". That is not pedantry, it is the mechanism. To check exhaustiveness the compiler has to know the complete list of cases, and it can only guarantee that within the library. Hence the convention — however many cases you have, they live together in one file. It looks odd the first time and then turns out to be convenient: the screen's whole state space is visible at a glance.",[175,1173,1174],{},[277,1175,1176],{},"Step three: the UI.",[182,1178,1180],{"className":184,"code":1179,"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,1181,1182,1199,1211,1233,1258,1282,1289],{"__ignoreMap":186},[191,1183,1184,1187,1191,1193,1196],{"class":193,"line":194},[191,1185,1186],{"class":201},"Widget",[191,1188,1190],{"class":1189},"sGLFI"," build",[191,1192,1043],{"class":205},[191,1194,1195],{"class":201},"BuildContext",[191,1197,1198],{"class":205}," context) {\n",[191,1200,1201,1205,1208],{"class":193,"line":209},[191,1202,1204],{"class":1203},"sVHd0","  return",[191,1206,1207],{"class":1203}," switch",[191,1209,1210],{"class":205}," (state) {\n",[191,1212,1213,1216,1219,1222,1225,1228,1230],{"class":193,"line":238},[191,1214,1215],{"class":201},"    Loading",[191,1217,1218],{"class":205},"() ",[191,1220,1221],{"class":197},"=>",[191,1223,1224],{"class":212}," const",[191,1226,1227],{"class":201}," AppSpinner",[191,1229,971],{"class":205},[191,1231,1232],{"class":234},",\n",[191,1234,1235,1238,1240,1243,1245,1248,1250,1253,1256],{"class":193,"line":251},[191,1236,1237],{"class":201},"    Failed",[191,1239,1043],{"class":205},[191,1241,1242],{"class":197},":",[191,1244,986],{"class":212},[191,1246,1247],{"class":205}," error) ",[191,1249,1221],{"class":197},[191,1251,1252],{"class":201}," ErrorView",[191,1254,1255],{"class":205},"(error)",[191,1257,1232],{"class":234},[191,1259,1260,1263,1265,1267,1269,1272,1274,1277,1280],{"class":193,"line":266},[191,1261,1262],{"class":201},"    Loaded",[191,1264,1043],{"class":205},[191,1266,1242],{"class":197},[191,1268,986],{"class":212},[191,1270,1271],{"class":205}," items) ",[191,1273,1221],{"class":197},[191,1275,1276],{"class":201}," ItemList",[191,1278,1279],{"class":205},"(items)",[191,1281,1232],{"class":234},[191,1283,1284,1287],{"class":193,"line":845},[191,1285,1286],{"class":205},"  }",[191,1288,235],{"class":234},[191,1290,1291],{"class":193,"line":858},[191,1292,269],{"class":205},[175,1294,1295,1296,1298,1299,1302,1303,1305,1306,1309],{},"Not one ",[188,1297,572],{},". Not one null check. The data is destructured in the pattern — ",[188,1300,1301],{},":final items"," — and arrives already typed. Inside the ",[188,1304,363],{}," branch the list ",[277,1307,1308],{},"exists",", rather than \"might exist.\" Compare that to what you would have on flags: a chain of checks whose order matters and whose reason nobody remembers.",[538,1311,1313],{"id":1312},"doing-this-on-a-project-with-forty-screens","Doing this on a project with forty screens",[175,1315,1316,1317,1320],{},"Do not rewrite everything. The rule: ",[277,1318,1319],{},"new screen, sealed from the start; old screen, when you are already in there fixing something."," A screen nobody has touched in two years, leave alone. It may well have impossible states, but they have either already fired or never will.",[175,1322,1323,1324,1327,1328,1331,1332,1337],{},"For the ones you do touch, priority comes down to one thing: ",[277,1325,1326],{},"how many places read that state."," One ",[188,1329,1330],{},"switch"," in one widget — rewriting barely pays, since you can see everything anyway. A state read from six places, across the UI, analytics, logging and a push handler, is where exhaustiveness earns its keep, because those are exactly the five out of six that get forgotten. If a screen already funnels its state through a single stream rather than ",[1333,1334,1336],"a",{"href":1335},"\u002Fblog\u002Fai-agents-struggle-with-flutter","a pyramid of flags",", that is the cheapest place to start.",[439,1339,1341],{"id":1340},"the-same-type-in-four-other-languages","The same type in four other languages",[175,1343,1344],{},"Not because you need a second language, but so it is clear this is not a Dart peculiarity or a Flutter-community fashion. Same type, three states, data attached to the state.",[175,1346,1347],{},[277,1348,1349],{},"Kotlin.",[182,1351,1354],{"className":1352,"code":1353,"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,1355,1356,1367,1371,1387,1391,1402,1415,1422,1426,1436,1453],{"__ignoreMap":186},[191,1357,1358,1360,1363],{"class":193,"line":194},[191,1359,954],{"class":212},[191,1361,1362],{"class":197}," interface",[191,1364,1366],{"class":1365},"sbgvK"," ProductsState\n",[191,1368,1369],{"class":193,"line":209},[191,1370,805],{"emptyLinePlaceholder":804},[191,1372,1373,1376,1379,1381,1384],{"class":193,"line":238},[191,1374,1375],{"class":212},"data",[191,1377,1378],{"class":197}," object",[191,1380,991],{"class":1365},[191,1382,1383],{"class":205}," : ",[191,1385,1386],{"class":1365},"ProductsState\n",[191,1388,1389],{"class":193,"line":251},[191,1390,805],{"emptyLinePlaceholder":804},[191,1392,1393,1395,1397,1399],{"class":193,"line":266},[191,1394,1375],{"class":212},[191,1396,957],{"class":197},[191,1398,1027],{"class":1365},[191,1400,1401],{"class":205},"(\n",[191,1403,1404,1407,1410,1413],{"class":193,"line":845},[191,1405,1406],{"class":197},"    val",[191,1408,1409],{"class":205}," error: ",[191,1411,1412],{"class":1365},"AppError",[191,1414,1232],{"class":205},[191,1416,1417,1420],{"class":193,"line":858},[191,1418,1419],{"class":205},") : ",[191,1421,1386],{"class":1365},[191,1423,1424],{"class":193,"line":1015},[191,1425,805],{"emptyLinePlaceholder":804},[191,1427,1428,1430,1432,1434],{"class":193,"line":1020},[191,1429,1375],{"class":212},[191,1431,957],{"class":197},[191,1433,1085],{"class":1365},[191,1435,1401],{"class":205},[191,1437,1438,1440,1443,1446,1448,1450],{"class":193,"line":1036},[191,1439,1406],{"class":197},[191,1441,1442],{"class":205}," items: ",[191,1444,1445],{"class":1365},"List",[191,1447,219],{"class":205},[191,1449,222],{"class":1365},[191,1451,1452],{"class":205},">,\n",[191,1454,1455,1457],{"class":193,"line":1057},[191,1456,1419],{"class":205},[191,1458,1386],{"class":1365},[175,1460,1461,1464,1465,487],{},[188,1462,1463],{},"when"," used as an expression has to cover every branch — exactly our ",[188,1466,1330],{},[175,1468,1469,1472,1473,1242],{},[277,1470,1471],{},"Rust."," More interesting, because in Rust this is the built-in ",[188,1474,753],{},[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,753],{"class":212},[191,1486,202],{"class":1365},[191,1488,206],{"class":234},[191,1490,1491,1493],{"class":193,"line":209},[191,1492,1215],{"class":1365},[191,1494,1232],{"class":234},[191,1496,1497,1499,1501,1503],{"class":193,"line":238},[191,1498,1237],{"class":1189},[191,1500,1043],{"class":234},[191,1502,1412],{"class":1365},[191,1504,1505],{"class":234},"),\n",[191,1507,1508,1510,1512,1515,1517,1519],{"class":193,"line":251},[191,1509,1262],{"class":1189},[191,1511,1043],{"class":234},[191,1513,1514],{"class":1365},"Vec",[191,1516,219],{"class":234},[191,1518,222],{"class":1365},[191,1520,1521],{"class":234},">),\n",[191,1523,1524],{"class":193,"line":266},[191,1525,269],{"class":234},[175,1527,1528,1529,1531,1532,1535,1536,1539],{},"In Rust an ",[188,1530,753],{}," ",[277,1533,1534],{},"is"," a sum with data from the start, with no extra ceremony. Miss a case in a ",[188,1537,1538],{},"match"," and it does not compile.",[175,1541,1542,1544],{},[277,1543,139],{},", the same thing with 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,753],{"class":212},[191,1556,202],{"class":1365},[191,1558,206],{"class":234},[191,1560,1561,1564],{"class":193,"line":209},[191,1562,1563],{"class":212},"    case",[191,1565,1566],{"class":1046}," loading\n",[191,1568,1569,1571,1574,1576,1578],{"class":193,"line":238},[191,1570,1563],{"class":212},[191,1572,1573],{"class":1046}," failed",[191,1575,1043],{"class":234},[191,1577,1412],{"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":1046}," 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],{},"And the exotic one, Idris"," — the ML syntax all of the above descend from:",[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],{},"Look at the vertical bar. It reads as \"or\". ",[277,1637,1638],{},"The sum type is literally written with an or-symbol"," — this is a sum long before anybody called it a sealed class.",[175,1641,1642],{},"Idris also does something none of the other four can:",[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,805],{"emptyLinePlaceholder":804},[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":845},[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 the type means a list ",[277,1684,1685],{},"guaranteed to have at least one element."," Not \"we checked\", not \"we agreed\" — you simply cannot construct ",[188,1688,363],{}," with an empty list. Remember this one; it comes back at the end.",[175,1691,1692],{},"One idea, five languages, exhaustiveness in all of them. If you are not on Dart, everything below works the same for you.",[439,1694,1696],{"id":1695},"exhaustiveness-which-is-what-all-of-this-was-for","Exhaustiveness, which is what all of this was for",[175,1698,1699],{},"A month later a requirement arrives: when the list comes back empty, show a placeholder with a button. That is not an error and it is not success-with-data. It is a fourth 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,986],{"class":212},[191,1710,957],{"class":197},[191,1712,1713],{"class":201}," Empty",[191,1715,994],{"class":197},[191,1717,202],{"class":201},[191,1719,206],{"class":205},[191,1721,1722,1724,1726,1728],{"class":193,"line":209},[191,1723,966],{"class":212},[191,1725,1713],{"class":201},[191,1727,971],{"class":205},[191,1729,235],{"class":234},[191,1731,1732],{"class":193,"line":238},[191,1733,269],{"class":205},[175,1735,1736],{},"One line. Nothing else changed. And the project does not build:",[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],{},"The compiler lists ",[277,1749,1750],{},"every"," place the new state is not handled. Not one of them — all of them.",[175,1753,1754,1755,1757,1758,1761],{},"Now, why this belongs in an article about AI-written code. \"Add an empty state to the list screen\" is exactly the size of task people hand to an agent without looking. The agent will add the class, update the ",[188,1756,1330],{}," it had in context, and ",[277,1759,1760],{},"not update the other two"," — the one in analytics and the one behind the refresh button. They were not in the prompt, so as far as it is concerned they do not exist.",[175,1763,1764],{},"On flags, that ships. There is no diff to catch it, because the diff only contains what the agent changed. Tests are green, because a state that did not exist a month ago has no tests by definition. You find out from a user six weeks later, in the form of \"sometimes I just get a blank screen.\"",[175,1766,1767],{},"On sealed classes it ships nowhere. It does not build.",[175,1769,1770,1771,1774],{},"And here is the part worth taking away. ",[277,1772,1773],{},"Exhaustiveness is not protection from AI. It is feedback for it."," What makes an agent bad at this is that it has no access to the list in your head of \"and also do not forget this bit over here.\" The compiler emits that list in machine-readable form, with files and line numbers. The agent hits red and fixes all three, which it is genuinely good at, because it was told exactly where. You are not stopping it from working — you are giving it the thing it was missing. The same mechanism helps a human once a week and an agent every twenty minutes.",[538,1776,1778],{"id":1777},"the-one-line-that-switches-it-all-off","The one line that switches it all off",[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":1203},"return",[191,1790,1207],{"class":1203},[191,1792,1210],{"class":205},[191,1794,1795,1798,1800,1802,1804,1806,1808],{"class":193,"line":209},[191,1796,1797],{"class":201},"  Loading",[191,1799,1218],{"class":205},[191,1801,1221],{"class":197},[191,1803,1224],{"class":212},[191,1805,1227],{"class":201},[191,1807,971],{"class":205},[191,1809,1232],{"class":234},[191,1811,1812,1815,1817,1819,1822,1824,1826],{"class":193,"line":238},[191,1813,1814],{"class":205},"  _ ",[191,1816,1221],{"class":197},[191,1818,1276],{"class":201},[191,1820,1821],{"class":205},"(state",[191,1823,487],{"class":234},[191,1825,1107],{"class":205},[191,1827,1232],{"class":234},[191,1829,1830,1833],{"class":193,"line":251},[191,1831,1832],{"class":205},"}",[191,1834,235],{"class":234},[175,1836,1837],{},"One underscore and exhaustiveness is dead. Quietly, with no warning. A catch-all handles everything by definition, so there is nothing left for the compiler to check. Add a fifth state, a sixth, a tenth — it builds, and silently falls through to here.",[175,1839,1840],{},"If you take exactly one review rule out of this article, take this one:",[1842,1843,1844],"blockquote",{},[175,1845,1846],{},[277,1847,1848],{},"A switch over a sealed type should not have a default branch.",[175,1850,1851],{},"It is the one line here you actually have to check by eye — and a lint rule catches it, so you do not even have to.",[439,1853,1855],{"id":1854},"where-algebraic-data-types-do-not-help","Where algebraic data types do not help",[175,1857,1858],{},"Four places where they do not work, or work against you. Without this section the rest is a sermon.",[175,1860,1861,1864,1865,1867,1868,1871],{},[277,1862,1863],{},"A sum does not cure a product."," You split the screen into three states, which is good. But ",[188,1866,363],{}," is still an ordinary class with fields, and if there are twelve of them and half are nullable, you have moved the swamp down one floor. ADTs are about ",[277,1869,1870],{},"which states exist",", not about what is inside a state.",[175,1873,1874,1877,1878,1880,1881,1883,1884,1887,1888,1890,1891,1893,1894,566,1897,1900],{},[277,1875,1876],{},"States that overlap — the most common mistake people make when they migrate."," A pull-to-refresh requirement lands: the list is already on screen and you are fetching a fresh one. Is that ",[188,1879,331],{}," or ",[188,1882,363],{},"? The naive answer is a ",[188,1885,1886],{},"Refreshing"," case with ",[188,1889,300],{}," in it. A week later you have ",[188,1892,1886],{},", ",[188,1895,1896],{},"RefreshingAfterError",[188,1898,1899],{},"FailedButHasCache"," — the same combinatorial explosion, now in classes, which is worse than flags because it is more verbose.",[175,1902,1903],{},"The right answer upsets people:",[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,986],{"class":212},[191,1914,957],{"class":197},[191,1916,1085],{"class":201},[191,1918,994],{"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,966],{"class":212},[191,1928,1085],{"class":201},[191,1930,1043],{"class":205},[191,1932,1047],{"class":1046},[191,1934,487],{"class":234},[191,1936,300],{"class":205},[191,1938,792],{"class":234},[191,1940,1941],{"class":205}," {",[191,1943,1047],{"class":1046},[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,1123],{"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],{},"Yes, the boolean is back, and that is ",[277,1994,1995],{},"fine."," It now lives inside a state where it means something: \"we have a list and we are refreshing\" is a real situation you need to express. And \"refreshing with no list\" no longer exists, because there is nowhere to put it.",[688,1998,2001],{":leading-icon":1999,":title":2000},"lucide:git-merge","Sums for what is mutually exclusive, products for what co-occurs",[175,2002,2003],{},"This is the rule that decides whether the refactor helps or hurts. Two things that can never be true at once want a sum — separate cases of a sealed class. Two things that are routinely true at the same time want a product — fields sitting side by side inside one case. Getting those backwards is the main way to end up worse off than you started: mutually exclusive states modelled as parallel booleans give you the eight-combination problem, and co-occurring facts modelled as separate cases give you a class per combination.",[175,2005,2006,2009,2010,2013,2014,2017,2018,2020],{},[277,2007,2008],{},"Types do not enforce every kind of invariant, and this matters more than the rest."," A sealed class expresses a ",[277,2011,2012],{},"structural"," invariant beautifully — which states exist, and which data travels with which state. It expresses nothing about invariants ",[277,2015,2016],{},"on values",": this list is sorted, the start date precedes the end date, the line items sum to the total, this string is a valid email. All of those are still held by a person, and no amount of ",[188,2019,954],{}," helps.",[175,2022,2023,2024,2026],{},"That Idris snippet is exactly the counter-example. ",[188,2025,1681],{}," — non-empty, in the type — is a value invariant pushed into the type system. The technique is called dependent types, and you could express \"sorted\" and \"start before end\" the same way. The price is a language you do not write in and, realistically, will not. In practice you reach for a different technique instead: a private constructor with validation, so a value of it cannot be created in an invalid shape.",[175,2028,2029,2030,2033,2034,2037,2038,2041,2042],{},"The same bucket holds ",[277,2031,2032],{},"system boundaries."," JSON off the wire is not typed. ADTs start ",[530,2035,2036],{},"after"," parsing, and a sealed class will not save you when the backend sends ",[188,2039,2040],{},"\"succes\""," with one \"s\" — explicit deserialization with hand-written keys will. The sentence worth keeping: ",[277,2043,2044],{},"ADTs eliminate impossible combinations of states, not impossible values.",[175,2046,2047,2050,2051,2053,2054,2056,2057,2060,2061],{},[277,2048,2049],{},"And over-engineering."," A sealed class with two cases and no data is an ",[188,2052,753],{},", and the ",[188,2055,753],{}," reads faster. A sealed class with one case is a class. An honest boolean stays an honest boolean — ",[188,2058,2059],{},"isSelected"," on a checkbox has exactly two states, both valid, and an ADT improves nothing there. The test is simple: count the combinations and cross out the meaningless ones. ",[277,2062,2063],{},"Nothing to cross out, leave it alone.",[175,2065,2066,2067,2070],{},"Finally, the honest cost: ",[277,2068,2069],{},"it is verbose."," Four states in Dart is about thirty lines, against four fields in the original. The \"slower to write\" half of that argument does not survive 2026 — that is precisely the work an agent does in seconds without mistakes. The \"slower to read\" half survives, and that is the price you pay.",[439,2072,2074],{"id":2073},"find-yours-in-thirty-minutes","Find yours in thirty minutes",[2076,2077],"how-to-schema",{":description":2078,":name":2079,":steps":2080,":total-time":2081},"A thirty-minute exercise that turns an abstract argument about types into a concrete count of the invalid states your code currently compiles.","How to find impossible states in your own codebase","[{\"name\": \"Pick one state class\", \"text\": \"Find a class with three or more fields that always travel together — booleans, nullables, or a mix. A screen state, a form state, a request wrapper. Pick the one whose state is read from the most places, not the one that looks worst.\"}, {\"name\": \"Write out every combination\", \"text\": \"For each field, count the states that matter: a bool has two, a nullable field has two, an enum has as many as it has values. Multiply them. Three fields is eight rows, four is sixteen. Write the rows down rather than reasoning about them, because the whole point is that nobody holds sixteen rows in their head.\"}, {\"name\": \"Cross out the ones that never happen\", \"text\": \"Go row by row and mark each one as a real situation the product can be in, or as something that never happens. Be strict: if you have to invent a story for why a row could occur, it does not occur.\"}, {\"name\": \"Count what is left\", \"text\": \"Every row you crossed out is an impossible state that compiles today. Five out of eight is the common result. That number is the size of the gap between what your type permits and what your domain allows.\"}, {\"name\": \"Grep for the default branch\", \"text\": \"Search the codebase for switch statements over that state and look for a default or underscore branch. Each one is a place where adding a new state will silently fall through instead of failing the build.\"}, {\"name\": \"Rewrite the most-read one first\", \"text\": \"Convert that single class to a sealed hierarchy with non-nullable fields, then fix every compiler error it produces. The errors are the list of places that were relying on a convention nobody wrote down.\"}]","PT30M",[2083,2084],"checklist",{":groups":2085,":subtitle":2086,":title":2087},"[{\"title\": \"The type itself\", \"items\": [\"Mutually exclusive situations are separate cases of a sealed type, not parallel booleans\", \"Every field inside a case is non-nullable, or the null means something specific and documented\", \"Data lives on the state that owns it, not alongside a status enum\", \"Facts that co-occur are fields inside one case, not a case per combination\"]}, {\"title\": \"Every switch over it\", \"items\": [\"No default branch and no underscore pattern over a sealed type\", \"No null-assertion operator on a field the state is supposed to guarantee\", \"Every reader of the state is covered, including analytics, logging and background handlers\"]}, {\"title\": \"What types cannot do for you\", \"items\": [\"Value invariants — sorted, non-empty, in range, well-formed — are enforced in a constructor, not assumed\", \"Parsing at the system boundary validates explicitly rather than trusting the shape\", \"Asserts are treated as a debugging aid, since they do not run in release builds\"]}]","Ten checks in three groups. Anything unchecked is a decision somebody has not made yet.","Reviewing a state class",[439,2089,2091],{"id":2090},"the-short-version","The short version",[175,2093,2094],{},"An impossible state is one your type permits and your domain forbids, which is the same thing as a broken invariant. It is expensive not because it breaks something at runtime, but because every convention that is not in the type gets re-derived by somebody at every single read.",[175,2096,2097],{},"The invariant always exists. The only question is whether a person is holding it — with a comment, a wiki page and an assert that does not run in release — or whether there is nothing to hold, because breaking it does not compile.",[175,2099,2100],{},"Three fields that travel together, eight combinations, three of them meaningful. A sealed class with non-nullable fields leaves exactly those three and makes the other five unbuildable. Exhaustiveness is the payoff: it turns \"somebody forgot to update the handling\" into \"the project does not build\", and it works the same regardless of who forgot. A default branch switches all of it off, quietly.",[175,2102,2103],{},"Here is the larger shift. Types used to be how you explained your thinking to a colleague opening the file six months later. Now they are the only way to explain it to something writing code faster than you can read it. The craft did not go anywhere — it moved up a level. You used to write the implementation. Now you write the boundaries the implementation has to stay inside, and anyone can fill them in, including a machine.",[439,2105,2107],{"id":2106},"frequently-asked-questions","Frequently asked questions",[2109,2110],"questions",{":items":2111},"[{\"title\": \"What is an impossible state in programming?\", \"text\": \"A state your type permits and your domain forbids. A screen state class with a loading boolean, a nullable list and a nullable error has eight combinations, but only three of them describe a situation the product can actually be in. The remaining five compile, pass review and ship. The same idea is more precisely called a broken invariant: the invariant is the rule that has to hold at all times, and the impossible state is what you get when the type does not enforce it.\"}, {\"title\": \"What are algebraic data types?\", \"text\": \"A way of describing how many values a type can hold. A product type is and — an ordinary class with fields, whose value count is the product of its field counts, so every field you add multiplies. A sum type is or — the value is one case or another and there is no third option, so the counts add instead of multiplying. An enum is a sum without data; a sealed class is a sum where each case carries its own data. The point of using sums is that you cannot construct a value representing a state that was never valid.\"}, {\"title\": \"Why not just use an enum with a status field?\", \"text\": \"Because it makes the arithmetic worse. An enum with three values next to a nullable list and a nullable error gives twelve combinations rather than eight, and still only three of them mean anything. The original problem is untouched: status can be success while the list is null, that compiles, and the UI ends up asserting that the field is non-null. An enum tells you which state you are in but leaves the data alongside it, tied to the state only by convention. A sealed class attaches the data to the state, so the list exists exactly where it means something.\"}, {\"title\": \"Do sealed classes in Dart have to live in one file?\", \"text\": \"In the same library, which in practice means the same file unless you deliberately split it with part. That is the mechanism rather than a style rule: to check exhaustiveness the compiler needs the complete list of subtypes, and it can only guarantee completeness within the library. The convention that follows is to keep every case of a state together in one file, which turns out to be convenient — the whole state space of a screen is visible at a glance.\"}, {\"title\": \"Why does a default branch break exhaustiveness checking?\", \"text\": \"Because a catch-all handles every case by definition, so there is nothing left for the compiler to verify. Once a switch over a sealed type has a default or underscore branch, adding a fifth or a tenth state compiles cleanly and silently falls through to that branch instead of failing the build. This is the single highest-value review rule on the subject: a switch over a sealed type should not have a default branch, and a lint rule can enforce it.\"}, {\"title\": \"Do sealed classes help with AI-generated code?\", \"text\": \"Yes, but not as a barrier. Exhaustiveness is feedback rather than protection. An agent asked to add a new state will update the switch it had in context and miss the ones it did not, because they were not in the prompt. With flags that ships silently, since the diff only shows what changed and a brand-new state has no tests. With a sealed type the build fails and the compiler names every unhandled location with a file and a line number, which is exactly the input an agent is good at acting on.\"}, {\"title\": \"Where do algebraic data types not help?\", \"text\": \"Four places. They do not fix a case that is itself a bloated product type with twelve nullable fields. They hurt when used for states that genuinely overlap, such as refreshing while data is already on screen, which wants a boolean inside the loaded case rather than a new case. They express nothing about value invariants such as sorted, non-empty or a valid email, which still need a validating constructor, and they start only after parsing, so they do nothing at a system boundary either, where the backend sends a misspelled status string. And they turn into over-engineering wherever an enum or an honest boolean would have done.\"}]",[175,2113,2114,2115,2118,2119,2123,2124,487],{},"If you want the wider list of what unsupervised agents do to Flutter code, this is number three on our ",[1333,2116,2117],{"href":1335},"seven-item breakdown",", and the process we use to keep it from happening is written up in ",[1333,2120,2122],{"href":2121},"\u002Fblog\u002Fbuilding-with-ai","how we build with AI",". This article is the companion to our video episode on the same subject; the previous one was ",[1333,2125,2127],{"href":2126},"\u002Fblog\u002Fssl-certificate-pinning-flutter","your SSL pinning probably is not working",[175,2129,2130],{},"Run the exercise and tell us how many rows you crossed out, and on which screen. Our bet is that most people land on the same three states we did.",[2132,2133,2134],"post-credits",{},[175,2135,2136,2137,2141,2142,2146],{},"Ilya Nixan is Founder & Lead Developer at ",[1333,2138,2140],{"href":2139},"\u002F","Nerdy Production",", a Flutter-first agency that builds and maintains apps across fintech, healthcare, and retail. We also run an ",[1333,2143,2145],{"href":2144},"\u002Fservices\u002Fai-code-audit","AI code audit"," for teams whose codebase grew faster than anyone could review it.",[2148,2149,2150],"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":2152},[2153,2154,2158,2161,2164,2165,2168,2169,2170,2171],{"id":441,"depth":209,"text":442},{"id":512,"depth":209,"text":513,"children":2155},[2156,2157],{"id":540,"depth":238,"text":541},{"id":673,"depth":238,"text":674},{"id":713,"depth":209,"text":714,"children":2159},[2160],{"id":767,"depth":238,"text":768},{"id":929,"depth":209,"text":930,"children":2162},[2163],{"id":1312,"depth":238,"text":1313},{"id":1340,"depth":209,"text":1341},{"id":1695,"depth":209,"text":1696,"children":2166},[2167],{"id":1777,"depth":238,"text":1778},{"id":1854,"depth":209,"text":1855},{"id":2073,"depth":209,"text":2074},{"id":2090,"depth":209,"text":2091},{"id":2106,"depth":209,"text":2107},"md",{"type":2174,"url":2175},"youtube","https:\u002F\u002Fyoutu.be\u002FiUI7vWX_22w",{},"An agent returned a state class with eight combinations and three that mean anything. How sealed classes and exhaustiveness make the other five unbuildable.","Impossible States in Dart: Sealed Classes and ADTs","One bool, two nullables, eight combinations, three that mean anything. The other five compile. Here is how to make them not compile.","\u002Fblog\u002Fimpossible-states-dart-sealed-classes",{"title":169,"description":177},"impossible-states-dart-sealed-classes","blog\u002Fimpossible-states-dart-sealed-classes","We gave an agent an ordinary task — a product list screen with loading, an error and data — and it returned a class with one boolean and two nullable fields. That is eight combinations, three of which mean anything; the other five compile, pass review, ship, and wait. This is what an impossible state is, why it is the same thing as a broken invariant, why the real cost is not runtime bugs but the unwritten conventions every reader has to reconstruct, and how sealed classes plus exhaustiveness turn a whole category of mistakes into a build failure. Also: why an enum plus nullable fields makes it worse, the one line that silently switches the check off, and the four places where algebraic data types do not help at all.","2026-08-31T12:00:00Z","software-engineering",null,"ZYaz4mYK0SHEZ-PhnzqGUAePpUqlMz4EXJL3APsAUXw",[2190,2247,2294,2344,2433,2485],{"id":2191,"bio":2192,"expertise":2197,"extension":2213,"links":2214,"meta":2218,"metaDescription":2219,"name":2224,"ogDescription":2227,"photo":2232,"role":2233,"seniority":2238,"skills":2239,"slug":2240,"specialization":2241,"stem":2240,"__hash__":2246},"team_members\u002Fdima.yaml",{"en":2193,"ru":2194,"es":2195,"nl":2196},"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",[2198,2201,2206,2208],{"en":2199,"ru":2199,"es":2200,"nl":2199},"State management","Gestión de estado",{"en":2202,"ru":2203,"es":2204,"nl":2205},"Application architecture","Архитектура приложений","Arquitectura de aplicaciones","Applicatiearchitectuur",{"en":2207,"ru":2207,"es":2207,"nl":2207},"WebRTC",{"en":2209,"ru":2210,"es":2211,"nl":2212},"XMPP messaging","Обмен сообщениями по XMPP","Mensajería XMPP","Berichten via XMPP","yaml",[2215],{"type":2216,"address":2217},"email","konopatov@nerdy.pro",{},{"en":2220,"ru":2221,"es":2222,"nl":2223},"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":2225,"ru":2226,"es":2225,"nl":2225},"Dima","Дима",{"en":2228,"ru":2229,"es":2230,"nl":2231},"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":2234,"ru":2235,"es":2236,"nl":2237},"Lead Flutter Developer","Ведущий Flutter-разработчик","Lead de desarrollo Flutter","Lead Flutter-developer","lead",[41,18,155,160,90,111,150,95],"dima",{"en":2242,"ru":2243,"es":2244,"nl":2245},"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":2248,"bio":2249,"expertise":2254,"extension":2213,"links":2187,"meta":2265,"metaDescription":2266,"name":2271,"ogDescription":2274,"photo":2279,"role":2280,"seniority":2285,"skills":2286,"slug":2287,"specialization":2288,"stem":2287,"__hash__":2293},"team_members\u002Fmasha.yaml",{"en":2250,"ru":2251,"es":2252,"nl":2253},"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",[2255,2260],{"en":2256,"ru":2257,"es":2258,"nl":2259},"UI\u002FUX design","UI\u002FUX-дизайн","Diseño UI\u002FUX","UI\u002FUX-ontwerp",{"en":2261,"ru":2262,"es":2263,"nl":2264},"Product copywriting","Продуктовый копирайтинг","Redacción de producto","Productteksten",{},{"en":2267,"ru":2268,"es":2269,"nl":2270},"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":2272,"ru":2273,"es":2272,"nl":2272},"Masha","Маша",{"en":2275,"ru":2276,"es":2277,"nl":2278},"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":2281,"ru":2282,"es":2283,"nl":2284},"Flutter Developer","Flutter-разработчик","Desarrolladora Flutter","Flutter-developer","middle",[41,18],"masha",{"en":2289,"ru":2290,"es":2291,"nl":2292},"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":2295,"bio":2296,"expertise":2301,"extension":2213,"links":2312,"meta":2315,"metaDescription":2316,"name":2321,"ogDescription":2324,"photo":2329,"role":2330,"seniority":2335,"skills":2336,"slug":2337,"specialization":2338,"stem":2337,"__hash__":2343},"team_members\u002Fmaxim.yaml",{"en":2297,"ru":2298,"es":2299,"nl":2300},"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",[2302,2307],{"en":2303,"ru":2304,"es":2305,"nl":2306},"Automated testing","Автоматизированное тестирование","Testing automatizado","Geautomatiseerd testen",{"en":2308,"ru":2309,"es":2310,"nl":2311},"High-load backend systems","Высоконагруженные бэкенд-системы","Sistemas backend de alta carga","Backendsystemen met hoge belasting",[2313],{"type":2216,"address":2314},"maxim@nerdy.pro",{},{"en":2317,"ru":2318,"es":2319,"nl":2320},"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":2322,"ru":2323,"es":2322,"nl":2322},"Maxim","Максим",{"en":2325,"ru":2326,"es":2327,"nl":2328},"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":2331,"ru":2332,"es":2333,"nl":2334},"Senior Software Engineer","Старший инженер-программист","Ingeniero de software senior","Senior software engineer","senior",[41,18,51,106,155,13,150],"maxim",{"en":2339,"ru":2340,"es":2341,"nl":2342},"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":2345,"bio":2346,"expertise":2351,"extension":2213,"links":2393,"meta":2405,"metaDescription":2406,"name":2411,"ogDescription":2414,"photo":2419,"role":2420,"seniority":2425,"skills":2426,"slug":170,"specialization":2427,"stem":170,"__hash__":2432},"team_members\u002Fnixan.yaml",{"en":2347,"ru":2348,"es":2349,"nl":2350},"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",[2352,2357,2362,2367,2372,2377,2382,2387,2389,2391],{"en":2353,"ru":2354,"es":2355,"nl":2356},"iOS development","Разработка под iOS","Desarrollo iOS","iOS-ontwikkeling",{"en":2358,"ru":2359,"es":2360,"nl":2361},"Software architecture","Архитектура ПО","Arquitectura de software","Softwarearchitectuur",{"en":2363,"ru":2364,"es":2365,"nl":2366},"Engineering team leadership","Руководство инженерными командами","Liderazgo de equipos de ingeniería","Leidinggeven aan engineeringteams",{"en":2368,"ru":2369,"es":2370,"nl":2371},"Payment systems","Платёжные системы","Sistemas de pago","Betaalsystemen",{"en":2373,"ru":2374,"es":2375,"nl":2376},"Card processing","Карточный процессинг","Procesamiento de tarjetas","Kaartverwerking",{"en":2378,"ru":2379,"es":2380,"nl":2381},"PCI-DSS compliance","Соответствие PCI-DSS","Cumplimiento de PCI-DSS","Naleving van PCI-DSS",{"en":2383,"ru":2384,"es":2385,"nl":2386},"NFC and contactless payments","NFC и бесконтактные платежи","NFC y pagos contactless","NFC en contactloos betalen",{"en":2388,"ru":2388,"es":2388,"nl":2388},"Host Card Emulation",{"en":2390,"ru":2390,"es":2390,"nl":2390},"EMV Contactless",{"en":2392,"ru":2392,"es":2392,"nl":2392},"AOSP",[2394,2397,2400,2403],{"type":2395,"url":2396},"github","https:\u002F\u002Fgithub.com\u002Fthenixan",{"type":2398,"url":2399},"linkedin","https:\u002F\u002Fwww.linkedin.com\u002Fin\u002Fthenixan\u002F",{"type":2401,"url":2402},"telegram","https:\u002F\u002Ft.me\u002Fthenixan",{"type":2216,"address":2404},"nixan@nerdy.pro",{},{"en":2407,"ru":2408,"es":2409,"nl":2410},"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":2412,"ru":2413,"es":2412,"nl":2412},"Ilya Nixan","Илья Никсан",{"en":2415,"ru":2416,"es":2417,"nl":2418},"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":2421,"ru":2422,"es":2423,"nl":2424},"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":2428,"ru":2429,"es":2430,"nl":2431},"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":2434,"bio":2435,"expertise":2440,"extension":2213,"links":2452,"meta":2457,"metaDescription":2458,"name":2463,"ogDescription":2466,"photo":2471,"role":2472,"seniority":2335,"skills":2477,"slug":2478,"specialization":2479,"stem":2478,"__hash__":2484},"team_members\u002Froma.yaml",{"en":2436,"ru":2437,"es":2438,"nl":2439},"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",[2441,2442,2447],{"en":2353,"ru":2354,"es":2355,"nl":2356},{"en":2443,"ru":2444,"es":2445,"nl":2446},"Applied AI","Прикладной AI","IA aplicada","Toegepaste AI",{"en":2448,"ru":2449,"es":2450,"nl":2451},"ERP and CRM systems","ERP- и CRM-системы","Sistemas ERP y CRM","ERP- en CRM-systemen",[2453,2455],{"type":2398,"url":2454},"https:\u002F\u002Fwww.linkedin.com\u002Fin\u002Fromanbatler\u002F",{"type":2216,"address":2456},"roma@nerdy.pro",{},{"en":2459,"ru":2460,"es":2461,"nl":2462},"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":2464,"ru":2465,"es":2464,"nl":2464},"Roman","Рома",{"en":2467,"ru":2468,"es":2469,"nl":2470},"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":2473,"ru":2474,"es":2475,"nl":2476},"Senior Mobile Developer","Старший мобильный разработчик","Desarrollador móvil senior","Senior mobiele developer",[7,41,18,141,146,106],"roma",{"en":2480,"ru":2481,"es":2482,"nl":2483},"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":2486,"bio":2487,"expertise":2492,"extension":2213,"links":2187,"meta":2503,"metaDescription":2504,"name":2509,"ogDescription":2512,"photo":2517,"role":2518,"seniority":2335,"skills":2523,"slug":2524,"specialization":2525,"stem":2524,"__hash__":2530},"team_members\u002Fxsox.yaml",{"en":2488,"ru":2489,"es":2490,"nl":2491},"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",[2493,2498],{"en":2494,"ru":2495,"es":2496,"nl":2497},"API design","Проектирование API","Diseño de APIs","API-ontwerp",{"en":2499,"ru":2500,"es":2501,"nl":2502},"Data modelling","Моделирование данных","Modelado de datos","Datamodellering",{},{"en":2505,"ru":2506,"es":2507,"nl":2508},"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":2510,"ru":2511,"es":2510,"nl":2510},"Eugene Xsox","Женя Xsox",{"en":2513,"ru":2514,"es":2515,"nl":2516},"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":2519,"ru":2520,"es":2521,"nl":2522},"Senior Backend Developer","Старший бэкенд-разработчик","Desarrollador backend senior","Senior backenddeveloper",[106,24,41,18,155,160,90,150],"xsox",{"en":2526,"ru":2527,"es":2528,"nl":2529},"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",[2532,2541,2548,2553,2563],{"id":2533,"extension":2213,"meta":2534,"name":2535,"slug":2539,"stem":2539,"__hash__":2540},"blog_topics\u002Fcybersecurity.yaml",{},{"en":2536,"ru":2537,"es":2538,"nl":2536},"Cybersecurity","Кибербезопасность","Ciberseguridad","cybersecurity","kt1rdsMy5W4b9MvGHm6mZVCr0inHidEuqaosPYmmk4E",{"id":2542,"extension":2213,"meta":2543,"name":2544,"slug":2546,"stem":2546,"__hash__":2547},"blog_topics\u002Fdevops.yaml",{},{"en":2545,"ru":2545,"es":2545,"nl":2545},"DevOps","devops","D3mxlZuFHKCGeszKZuWCiQmHmP5EiCvzg1qEAIQhnyU",{"id":2549,"extension":2213,"meta":2550,"name":2551,"slug":41,"stem":41,"__hash__":2552},"blog_topics\u002Fflutter.yaml",{},{"en":39,"ru":39,"es":39,"nl":39},"OOeALAmwFuEqByuJvTGk4g15FIHENPfYMdpfZh_HTAE",{"id":2554,"extension":2213,"meta":2555,"name":2556,"slug":2561,"stem":2561,"__hash__":2562},"blog_topics\u002Ffor-founders.yaml",{},{"en":2557,"ru":2558,"es":2559,"nl":2560},"For Founders","Для основателей","Para fundadores","Voor oprichters","for-founders","lp2u6Ol2qF4DGxpB83HxbbGzWioT-e40mJhD4lr28-U",{"id":2564,"extension":2213,"meta":2565,"name":2566,"slug":2186,"stem":2186,"__hash__":2571},"blog_topics\u002Fsoftware-engineering.yaml",{},{"en":2567,"ru":2568,"es":2569,"nl":2570},"Software Engineering","Разработка ПО","Ingeniería de software","Software-engineering","aZMb3aUDptoFQk_cEE6l5H4wrYcgZuBYUubmKqOAaaU",{"data":2573,"body":2574},{},{"type":2575,"children":2576},"root",[2577,2583,2602,2613,2642],{"type":2578,"tag":175,"props":2579,"children":2580},"element",{},[2581],{"type":1742,"value":2582},"Ilya founded Nerdy Production and leads its engineering. He has been building software since 2010 and shipping production Flutter since 2018.",{"type":2578,"tag":175,"props":2584,"children":2585},{},[2586,2588,2593,2595,2600],{"type":1742,"value":2587},"Before 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, ",{"type":2578,"tag":2589,"props":2590,"children":2592},"term",{"slug":2591},"pci-dss",[],{"type":1742,"value":2594}," scope, and contactless payments — including building contactless card payments on Android via ",{"type":2578,"tag":2589,"props":2596,"children":2598},{"slug":2597},"host-card-emulation",[2599],{"type":1742,"value":2388},{"type":1742,"value":2601}," over ISO\u002FIEC 14443, with EMV Contactless (Visa PayWave) on top.",{"type":2578,"tag":175,"props":2603,"children":2604},{},[2605,2607,2611],{"type":1742,"value":2606},"He 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 ",{"type":2578,"tag":2589,"props":2608,"children":2610},{"slug":2609},"aosp",[],{"type":1742,"value":2612},", giving him a low-level view of Android most app developers never touch.",{"type":2578,"tag":175,"props":2614,"children":2615},{},[2616,2618,2624,2626,2632,2634,2640],{"type":1742,"value":2617},"Today he leads delivery on the agency's flagship apps — from the chart-heavy fintech UI of ",{"type":2578,"tag":1333,"props":2619,"children":2621},{"href":2620},"\u002Fportfolio\u002Fextraetf",[2622],{"type":1742,"value":2623},"ExtraETF",{"type":1742,"value":2625}," to the fully custom design system of ",{"type":2578,"tag":1333,"props":2627,"children":2629},{"href":2628},"\u002Fportfolio\u002Farcana",[2630],{"type":1742,"value":2631},"Arcana",{"type":1742,"value":2633},". He writes most of the essays on this blog and maintains the agency's open-source work, including the ",{"type":2578,"tag":1333,"props":2635,"children":2637},{"href":2636},"\u002Fopen-source\u002Fdxpdf",[2638],{"type":1742,"value":2639},"dxpdf",{"type":1742,"value":2641}," DOCX-to-PDF engine.",{"type":2578,"tag":175,"props":2643,"children":2644},{},[2645,2647,2652,2654,2659,2660,2665,2666,2671,2672,2677,2678,2683,2685,2690],{"type":1742,"value":2646},"He works across ",{"type":2578,"tag":1333,"props":2648,"children":2650},{"href":2649},"\u002Ftechnologies\u002Fflutter",[2651],{"type":1742,"value":39},{"type":1742,"value":2653},", native iOS and Android, ",{"type":2578,"tag":1333,"props":2655,"children":2657},{"href":2656},"\u002Ftechnologies\u002Fgo",[2658],{"type":1742,"value":49},{"type":1742,"value":1893},{"type":2578,"tag":1333,"props":2661,"children":2663},{"href":2662},"\u002Ftechnologies\u002Frust",[2664],{"type":1742,"value":124},{"type":1742,"value":1893},{"type":2578,"tag":1333,"props":2667,"children":2669},{"href":2668},"\u002Ftechnologies\u002Ftypescript",[2670],{"type":1742,"value":153},{"type":1742,"value":1893},{"type":2578,"tag":1333,"props":2673,"children":2675},{"href":2674},"\u002Ftechnologies\u002Fkotlin",[2676],{"type":1742,"value":69},{"type":1742,"value":1893},{"type":2578,"tag":1333,"props":2679,"children":2681},{"href":2680},"\u002Ftechnologies\u002Fkubernetes",[2682],{"type":1742,"value":78},{"type":1742,"value":2684},", and ",{"type":2578,"tag":1333,"props":2686,"children":2688},{"href":2687},"\u002Ftechnologies\u002Fdocker",[2689],{"type":1742,"value":27},{"type":1742,"value":2691},", with a focus on app architecture, cross-platform delivery, and building teams that ship.",[2693,2705,2718,2733,2747,2761,2777,2789,2803,2816,2830,2843,2859,2872,2888,2901,2912,2922,2935,2945,2956,2971,2980,2993,3006,3013,3024,3036,3047,3062,3074,3087,3099,3111,3123,3135,3146,3157,3168,3179,3191,3201,3212,3225,3237,3246,3258,3270,3280,3291,3304,3313,3324],{"slug":2609,"term":2392,"definition":2694,"category":2695,"aliases":2696,"links":2698,"related":2702,"readMore":-1,"target":2703,"hasArticle":2704},"The Android Open Source Project — Android without the Google layer on top, which anyone may fork. Point-of-sale terminals, kiosks and in-car systems run on forks of it, and working at that level exposes parts of the OS an app developer never sees.","platform",[2697],"Android Open Source Project",[2699],{"kind":2700,"url":2701},"website","https:\u002F\u002Fsource.android.com\u002F",[2597],"\u002Fglossary#aosp",false,{"slug":2706,"term":2707,"definition":2708,"category":2695,"aliases":2709,"links":2710,"related":2714,"readMore":-1,"target":2717,"hasArticle":2704},"app-clips","App Clips","An Apple feature that runs a small slice of an iOS app — under 15 MB — without installing the whole thing. Invoked from a QR code, an NFC tag or a link, for when the first thing a user does should not require a store visit.",[],[2711],{"kind":2712,"url":2713},"documentation","https:\u002F\u002Fdeveloper.apple.com\u002Fapp-clips\u002F",[2715,2716],"deep-linking","install-referrer","\u002Fglossary#app-clips",{"slug":2719,"term":2720,"definition":2721,"category":2695,"aliases":2722,"links":2723,"related":2729,"readMore":-1,"target":2732,"hasArticle":2704},"bigquery","BigQuery","Google Cloud's analytics warehouse. You point SQL at billions of rows and it scans them in seconds, on storage held separately from the machines doing the querying — which is what keeps reporting and exploration off the database that is serving live traffic.",[],[2724,2726],{"kind":2700,"url":2725},"https:\u002F\u002Fcloud.google.com\u002Fbigquery",{"kind":2727,"url":2728},"wikipedia","https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FBigQuery",[2730,2731],"object-storage","elasticsearch","\u002Fglossary#bigquery",{"slug":2734,"term":2735,"definition":2736,"category":2737,"aliases":2738,"links":2742,"related":2745,"readMore":-1,"target":2746,"hasArticle":2704},"ci-cd","CI\u002FCD","Automation that builds, tests and ships every change without anyone running commands by hand. On mobile it is what turns a release into a button press instead of an afternoon of someone else being unavailable.","practice",[2739,2740,2741],"CI","continuous integration","continuous delivery",[2743],{"kind":2727,"url":2744},"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FCI\u002FCD",[],"\u002Fglossary#ci-cd",{"slug":2748,"term":2749,"definition":2750,"category":2751,"aliases":2752,"links":2754,"related":2757,"readMore":-1,"target":2760,"hasArticle":2704},"crud","CRUD","Create, read, update, delete — the four operations behind almost every form and admin screen. Shorthand for the routine data-management half of an app, as opposed to the parts carrying real domain logic.","architecture",[2753],"Create, Read, Update, Delete",[2755],{"kind":2727,"url":2756},"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FCreate,_read,_update_and_delete",[2758,2759],"rest","graphql","\u002Fglossary#crud",{"slug":2762,"term":2763,"definition":2764,"category":2751,"aliases":2765,"links":2770,"related":2773,"readMore":-1,"target":2776,"hasArticle":2704},"container-registry","Container registry","A hosted store for container images, addressed by name and tag — Docker Hub, GitHub Container Registry, or a cloud provider's own. Pushing a build there turns 'works on my machine' into an image anyone can pull and run unchanged.",[2766,2767,2768,2769],"image registry","Docker registry","GHCR","ghcr.io",[2771],{"kind":2712,"url":2772},"https:\u002F\u002Fdocs.github.com\u002Fen\u002Fpackages\u002Fworking-with-a-github-packages-registry\u002Fworking-with-the-container-registry",[2774,2775],"dev-container","multi-arch-image","\u002Fglossary#container-registry",{"slug":2715,"term":2778,"definition":2779,"category":2695,"aliases":2780,"links":2784,"related":2787,"readMore":2788,"target":2788,"hasArticle":804},"Deep linking","A link that opens a specific screen inside an installed app instead of its home screen or a web page. Deferred deep linking survives an install, so a tap that leads through the app store still lands on the right screen.",[2781,2782,2783],"deferred deep linking","universal links","Android App Links",[2785],{"kind":2727,"url":2786},"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FMobile_deep_linking",[2706,2716],"\u002Fglossary\u002Fdeep-linking",{"slug":2774,"term":2790,"definition":2791,"category":2737,"aliases":2792,"links":2796,"related":2801,"readMore":-1,"target":2802,"hasArticle":2704},"Dev Container","A development environment described once in devcontainer.json — the OS, tools, and runtime versions a project needs — and opened identically inside a container by every contributor's editor, instead of a setup guide everyone interprets differently.",[2793,2794,2795],"devcontainer.json","Dev Containers","VS Code Dev Containers",[2797,2799],{"kind":2700,"url":2798},"https:\u002F\u002Fcontainers.dev\u002F",{"kind":2712,"url":2800},"https:\u002F\u002Fcode.visualstudio.com\u002Fdocs\u002Fdevcontainers\u002Fcontainers",[2762],"\u002Fglossary#dev-container",{"slug":2731,"term":2804,"definition":2805,"category":2695,"aliases":2806,"links":2809,"related":2814,"readMore":-1,"target":2815,"hasArticle":2704},"Elasticsearch","A search and analytics engine that indexes records so they can be filtered and searched interactively instead of scanned. What you reach for when the question is \"show me these particular sessions, narrowed six ways\" rather than \"sum this column\".",[2807,2808],"Elastic","ELK",[2810,2812],{"kind":2700,"url":2811},"https:\u002F\u002Fwww.elastic.co\u002Felasticsearch",{"kind":2727,"url":2813},"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FElasticsearch",[2719],"\u002Fglossary#elasticsearch",{"slug":2817,"term":2818,"definition":2819,"category":2751,"aliases":2820,"links":2823,"related":2826,"readMore":-1,"target":2829,"hasArticle":2704},"end-to-end-encryption","End-to-end encryption","Encryption applied on the sending device and undone only on the receiving one, so the service carrying the message cannot read it — not under subpoena, not after a breach. It protects the content and never the metadata.",[2821,2822],"E2EE","end-to-end encrypted",[2824],{"kind":2727,"url":2825},"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FEnd-to-end_encryption",[2827,2828],"tls","jwt","\u002Fglossary#end-to-end-encryption",{"slug":2831,"term":2832,"definition":2833,"category":2751,"aliases":2834,"links":2837,"related":2838,"readMore":-1,"target":2842,"hasArticle":2704},"fan-out","Fan-out","Reading an upstream source once and delivering each update to every client subscribed to it. The naive version writes to subscribers in a loop and stalls the moment one socket is slow; a real one buffers per client and drops whoever cannot keep up.",[2835,2836],"fanout","broadcast",[],[2839,2840,2841],"websocket","pub-sub","server-sent-events","\u002Fglossary#fan-out",{"slug":2844,"term":2845,"definition":2846,"category":2737,"aliases":2847,"links":2851,"related":2854,"readMore":-1,"target":2858,"hasArticle":2704},"feature-flags","Feature flags","Switches that turn functionality on or off from configuration rather than from a release. They let one binary behave differently per brand, market or user, and let a risky feature be shut off without shipping a new build through review.",[2848,2849,2850],"feature flag","feature toggle","feature gating",[2852],{"kind":2727,"url":2853},"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FFeature_toggle",[2855,2856,2857],"staged-rollout","white-label","multi-tenancy","\u002Fglossary#feature-flags",{"slug":2860,"term":2861,"definition":2862,"category":2751,"aliases":2863,"links":2867,"related":2870,"readMore":-1,"target":2871,"hasArticle":2704},"floating-point","Floating point","The IEEE 754 binary format behind double and float. It cannot hold 0.1 exactly, so 0.1 + 0.2 is 0.30000000000000004 — invisible in graphics and fatal in money, which belongs in integer minor units or a decimal type instead.",[2864,2865,2866],"IEEE 754","double","floating-point arithmetic",[2868],{"kind":2727,"url":2869},"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FIEEE_754",[],"\u002Fglossary#floating-point",{"slug":2873,"term":2874,"definition":2875,"category":2737,"aliases":2876,"links":2879,"related":2884,"readMore":-1,"target":2887,"hasArticle":2704},"gdpr","GDPR","The EU regulation covering personal data of people in the EU: a lawful basis for collecting it, real consent for tracking, and rights to see and delete it. It follows your users, not your servers, so it applies wherever the company is registered.",[2877,2878],"General Data Protection Regulation","data protection",[2880,2882],{"kind":2700,"url":2881},"https:\u002F\u002Fgdpr.eu\u002F",{"kind":2727,"url":2883},"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FGeneral_Data_Protection_Regulation",[2591,2885,2886],"soc-2","hipaa","\u002Fglossary#gdpr",{"slug":2889,"term":2890,"definition":2891,"category":2737,"aliases":2892,"links":2896,"related":2899,"readMore":-1,"target":2900,"hasArticle":2704},"golden-test","Golden test","A test that renders a widget and compares the result pixel for pixel against a stored reference image. In Flutter it is the cheapest way to answer whether a redesign broke the empty state at 320pt, in dark mode, at 200% text scale.",[2893,2894,2895],"golden tests","screenshot test","snapshot test",[2897],{"kind":2712,"url":2898},"https:\u002F\u002Fapi.flutter.dev\u002Fflutter\u002Fflutter_test\u002FmatchesGoldenFile.html",[2734],"\u002Fglossary#golden-test",{"slug":2759,"term":2902,"definition":2903,"category":2751,"aliases":2904,"links":2905,"related":2910,"readMore":-1,"target":2911,"hasArticle":2704},"GraphQL","A query language for APIs where the client names exactly the fields it wants and gets one response shaped to match. It removes the over-fetching REST endpoints drift into, and adds a failure mode of its own: an unbounded query that walks the whole data model.",[],[2906,2908],{"kind":2700,"url":2907},"https:\u002F\u002Fgraphql.org\u002F",{"kind":2727,"url":2909},"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FGraphQL",[2758],"\u002Fglossary#graphql",{"slug":2886,"term":2913,"definition":2914,"category":2737,"aliases":2915,"links":2917,"related":2920,"readMore":-1,"target":2921,"hasArticle":2704},"HIPAA","The US law governing protected health information — how it may be stored, transmitted, logged and disclosed. Like PCI-DSS it is an architectural constraint chosen at the start, not a policy document added before launch.",[2916],"Health Insurance Portability and Accountability Act",[2918],{"kind":2727,"url":2919},"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FHealth_Insurance_Portability_and_Accountability_Act",[2591,2873,2885],"\u002Fglossary#hipaa",{"slug":2923,"term":2924,"definition":2925,"category":2751,"aliases":2926,"links":2929,"related":2932,"readMore":-1,"target":2934,"hasArticle":2704},"headless-cms","Headless CMS","A content system with an editor and an API but no front end of its own. Editors publish in one place, and the site or app renders that content itself — so the presentation layer is yours rather than the CMS vendor's.",[2927,2928],"headless content management system","content API",[2930],{"kind":2727,"url":2931},"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FHeadless_content_management_system",[2933,2758],"server-side-rendering","\u002Fglossary#headless-cms",{"slug":2597,"term":2388,"definition":2936,"category":2695,"aliases":2937,"links":2940,"related":2943,"readMore":-1,"target":2944,"hasArticle":2704},"Letting an Android phone act as a contactless card over NFC in software, with no hardware secure element. It is how a wallet app pays at a terminal: the phone speaks the same EMV contactless protocol the plastic card would have.",[2938,2390,2939],"HCE","contactless payments",[2941],{"kind":2712,"url":2942},"https:\u002F\u002Fdeveloper.android.com\u002Fdevelop\u002Fconnectivity\u002Fnfc\u002Fhce",[2591,2609],"\u002Fglossary#host-card-emulation",{"slug":2946,"term":2947,"definition":2948,"category":2695,"aliases":2949,"links":2950,"related":2953,"readMore":-1,"target":2955,"hasArticle":2704},"impeller","Impeller","The rendering engine Flutter uses today, default on iOS since 2023 and on Android since 2024. It compiles its shaders ahead of time instead of during the first animation, which removed the shader-compilation jank that was Flutter's most visible production problem.",[],[2951],{"kind":2712,"url":2952},"https:\u002F\u002Fdocs.flutter.dev\u002Fperf\u002Fimpeller",[2954],"skia","\u002Fglossary#impeller",{"slug":2957,"term":2958,"definition":2959,"category":2960,"aliases":2961,"links":2965,"related":2968,"readMore":-1,"target":2970,"hasArticle":2704},"in-app-purchase","In-app purchase","Selling digital goods or a subscription through the Apple or Google billing that both stores require for digital content and take a commission on. The hard part is never the purchase; it is restoring it on a new device and keeping entitlement state honest.","business",[2962,2963,2964],"IAP","in-app purchases","in-app subscription",[2966],{"kind":2712,"url":2967},"https:\u002F\u002Fdeveloper.apple.com\u002Fin-app-purchase\u002F",[2969],"product-market-fit","\u002Fglossary#in-app-purchase",{"slug":2716,"term":2972,"definition":2973,"category":2695,"aliases":2974,"links":2975,"related":2978,"readMore":-1,"target":2979,"hasArticle":2704},"Install Referrer","A Google Play API that hands a freshly installed Android app the campaign parameters from the link that led to the install. The Android half of deferred deep linking, and the dependable way to attribute where a user came from.",[],[2976],{"kind":2712,"url":2977},"https:\u002F\u002Fdeveloper.android.com\u002Fgoogle\u002Fplay\u002Finstallreferrer",[2715,2706],"\u002Fglossary#install-referrer",{"slug":2828,"term":2981,"definition":2982,"category":2983,"aliases":2984,"links":2986,"related":2991,"readMore":-1,"target":2992,"hasArticle":2704},"JWT","A signed token carrying its own claims, so a server can tell who a request belongs to without looking a session up. Standard for mobile authentication. The signature proves it was not altered; it does not hide what is inside.","protocol",[2985],"JSON Web Token",[2987,2989],{"kind":2700,"url":2988},"https:\u002F\u002Fjwt.io\u002F",{"kind":2727,"url":2990},"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FJSON_Web_Token",[2591],"\u002Fglossary#jwt",{"slug":2994,"term":2995,"definition":2996,"category":2737,"aliases":2997,"links":3001,"related":3004,"readMore":-1,"target":3005,"hasArticle":2704},"kyc","KYC","Know Your Customer — the identity checks a regulated financial product runs before it lets anyone move money: document capture, liveness, sanctions and anti-money-laundering screening. It shapes onboarding more than any design decision does.",[2998,2999,3000],"Know Your Customer","AML","KYC\u002FAML",[3002],{"kind":2727,"url":3003},"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FKnow_your_customer",[2591,2873],"\u002Fglossary#kyc",{"slug":76,"term":74,"definition":3007,"category":2695,"aliases":3008,"links":3010,"related":3011,"readMore":3012,"target":3012,"hasArticle":2704},"Sharing business logic written in Kotlin across Android, iOS and the server while each platform keeps its own native UI. The alternative to Flutter when the interface has to be native but the rules behind it do not.",[3009],"KMP",[],[],"\u002Ftechnologies\u002Fkmp",{"slug":3014,"term":3015,"definition":3016,"category":2960,"aliases":3017,"links":3019,"related":3022,"readMore":-1,"target":3023,"hasArticle":2704},"mvp","MVP","The smallest version of a product that can go in front of real users and still answer the question you built it to answer. A decision about scope, not about quality — an MVP still has to work.",[3018],"minimum viable product",[3020],{"kind":2727,"url":3021},"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FMinimum_viable_product",[2856],"\u002Fglossary#mvp",{"slug":2775,"term":3025,"definition":3026,"category":2751,"aliases":3027,"links":3031,"related":3034,"readMore":-1,"target":3035,"hasArticle":2704},"Multi-architecture image","A single image tag that resolves to different binaries per CPU architecture — linux\u002Famd64 and linux\u002Farm64 are the common pair — so the same docker pull works unchanged on Intel\u002FAMD servers and Apple Silicon laptops.",[3028,3029,3030],"multi-arch build","multi-platform image","linux\u002Famd64 + linux\u002Farm64",[3032],{"kind":2712,"url":3033},"https:\u002F\u002Fdocs.docker.com\u002Fbuild\u002Fbuilding\u002Fmulti-platform\u002F",[2762],"\u002Fglossary#multi-arch-image",{"slug":2857,"term":3037,"definition":3038,"category":2751,"aliases":3039,"links":3042,"related":3045,"readMore":-1,"target":3046,"hasArticle":2704},"Multi-tenancy","One deployment serving many customers, each seeing only its own data, configuration and enabled features because tenant context is resolved per request. It is what makes a fleet of branded apps one product instead of many forks.",[3040,3041],"multi-tenant","tenant",[3043],{"kind":2727,"url":3044},"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FMultitenancy",[2856,2844],"\u002Fglossary#multi-tenancy",{"slug":3048,"term":3049,"definition":3050,"category":2983,"aliases":3051,"links":3055,"related":3060,"readMore":-1,"target":3061,"hasArticle":2704},"oauth","OAuth","The standard behind Sign in with Apple, Google and the rest: the user authorises your app at a provider they already trust, and your app receives a token instead of their password. Nobody invents a new credential and you never store one.",[3052,3053,3054],"OAuth 2.0","social login","Sign in with Apple",[3056,3058],{"kind":2700,"url":3057},"https:\u002F\u002Foauth.net\u002F2\u002F",{"kind":2727,"url":3059},"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FOAuth",[2828],"\u002Fglossary#oauth",{"slug":2730,"term":3063,"definition":3064,"category":2751,"aliases":3065,"links":3069,"related":3072,"readMore":-1,"target":3073,"hasArticle":2704},"Object storage","Storage that holds a whole file under a key rather than in a filesystem tree — Amazon S3 and the many services that speak its API. Cheap, effectively unlimited, and the usual home for raw events, backups and media: written once, read rarely, kept forever.",[3066,3067,3068],"S3","S3-compatible storage","blob storage",[3070],{"kind":2727,"url":3071},"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FObject_storage",[2719],"\u002Fglossary#object-storage",{"slug":2591,"term":3075,"definition":3076,"category":2737,"aliases":3077,"links":3080,"related":3085,"readMore":-1,"target":3086,"hasArticle":2704},"PCI-DSS","The card industry security standard binding anyone who stores, processes or transmits card data. Most apps stay out of its scope on purpose, by handing card entry to a certified payment provider instead.",[3078,3079],"PCI DSS","Payment Card Industry Data Security Standard",[3081,3083],{"kind":2700,"url":3082},"https:\u002F\u002Fwww.pcisecuritystandards.org\u002F",{"kind":2727,"url":3084},"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FPayment_Card_Industry_Data_Security_Standard",[2828],"\u002Fglossary#pci-dss",{"slug":3088,"term":3089,"definition":3090,"category":2695,"aliases":3091,"links":3094,"related":3097,"readMore":-1,"target":3098,"hasArticle":2704},"platform-channels","Platform channels","The bridge a Flutter app uses to call native iOS and Android code — Keychain and Keystore, biometrics, payment sheets, any SDK without a Dart package. Routine work but real work, and the first place an engineer who never left Dart will stall.",[3092,3093],"platform channel","method channel",[3095],{"kind":2712,"url":3096},"https:\u002F\u002Fdocs.flutter.dev\u002Fplatform-integration\u002Fplatform-channels",[76],"\u002Fglossary#platform-channels",{"slug":3100,"term":3101,"definition":3102,"category":2983,"aliases":3103,"links":3106,"related":3109,"readMore":-1,"target":3110,"hasArticle":2704},"post-quantum-cryptography","Post-quantum cryptography","Encryption algorithms built to stay secure against a future quantum computer, now standardized by NIST. The migration is urgent ahead of the hardware because traffic captured today can be decrypted once such a machine exists.",[3104,3105],"PQC","post-quantum crypto",[3107],{"kind":2727,"url":3108},"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FPost-quantum_cryptography",[2827,2817],"\u002Fglossary#post-quantum-cryptography",{"slug":2969,"term":3112,"definition":3113,"category":2960,"aliases":3114,"links":3117,"related":3120,"readMore":-1,"target":3122,"hasArticle":2704},"Product-market fit","The point at which a product has demonstrably found people who want it — they use it, come back, and pay. Before it, engineering answers a question; after it, engineering answers demand.",[3115,3116],"PMF","product\u002Fmarket fit",[3118],{"kind":2727,"url":3119},"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FProduct-market_fit",[3014,3121],"time-to-market","\u002Fglossary#product-market-fit",{"slug":3124,"term":3125,"definition":3126,"category":2737,"aliases":3127,"links":3129,"related":3132,"readMore":-1,"target":3134,"hasArticle":2704},"prompt-injection","Prompt injection","An attack where text supplied by a user is read by a language model as instructions rather than as data, steering it past its own rules. The LLM-era sibling of SQL injection, and it appears wherever user input is concatenated into a prompt.",[3128],"injection attack",[3130],{"kind":2727,"url":3131},"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FPrompt_injection",[3133],"rate-limiting","\u002Fglossary#prompt-injection",{"slug":2840,"term":3136,"definition":3137,"category":2751,"aliases":3138,"links":3141,"related":3144,"readMore":-1,"target":3145,"hasArticle":2704},"Pub\u002FSub","A messaging pattern where a producer publishes an event and any number of consumers read it independently, with a broker in between. The producer never waits for them, which is how a request path stays fast while slower work happens behind it.",[3139,3140],"publish\u002Fsubscribe","Google Cloud Pub\u002FSub",[3142],{"kind":2727,"url":3143},"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FPublish%E2%80%93subscribe_pattern",[],"\u002Fglossary#pub-sub",{"slug":2758,"term":3147,"definition":3148,"category":2751,"aliases":3149,"links":3152,"related":3155,"readMore":-1,"target":3156,"hasArticle":2704},"REST","The conventional style for HTTP APIs: a URL names a resource and the HTTP verb says what to do with it. The default way an app talks to a backend, and what most third-party integrations expect to find.",[3150,3151],"REST API","Representational State Transfer",[3153],{"kind":2727,"url":3154},"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FREST",[2839,2841],"\u002Fglossary#rest",{"slug":3133,"term":3158,"definition":3159,"category":2737,"aliases":3160,"links":3163,"related":3166,"readMore":-1,"target":3167,"hasArticle":2704},"Rate limiting","A cap on how many requests one caller may make in a given window. It is what stops a single enthusiastic user, a scraper or a bot from spending a month of paid API budget in an afternoon, and it has to live on your side of the integration.",[3161,3162],"rate limit","throttling",[3164],{"kind":2727,"url":3165},"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FRate_limiting",[3124],"\u002Fglossary#rate-limiting",{"slug":2885,"term":3169,"definition":3170,"category":2737,"aliases":3171,"links":3174,"related":3177,"readMore":-1,"target":3178,"hasArticle":2704},"SOC 2","An external auditor report on how an organisation handles customer data — security, availability, confidentiality — rather than a certificate you buy. Enterprise buyers ask for it, and it constrains architecture long before the audit itself does.",[3172,3173],"SOC2","System and Organization Controls",[3175],{"kind":2727,"url":3176},"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FSystem_and_Organization_Controls",[2591,2873,2886],"\u002Fglossary#soc-2",{"slug":3180,"term":3181,"definition":3182,"category":2960,"aliases":3183,"links":3186,"related":3189,"readMore":-1,"target":3190,"hasArticle":2704},"saas","SaaS","Software sold as an ongoing subscription to a hosted product rather than as a one-off license the customer installs and runs. The vendor operates the servers, ships updates continuously, and bills per seat or per usage.",[3184,3185],"Software as a Service","software-as-a-service",[3187],{"kind":2727,"url":3188},"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FSoftware_as_a_service",[2857,2856],"\u002Fglossary#saas",{"slug":2841,"term":3192,"definition":3193,"category":2983,"aliases":3194,"links":3196,"related":3199,"readMore":-1,"target":3200,"hasArticle":2704},"Server-Sent Events","A one-way stream from server to client over an ordinary HTTP connection. Simpler than a WebSocket and enough wherever only the server has something to say — a progress feed, an AI response arriving token by token.",[3195],"SSE",[3197],{"kind":2727,"url":3198},"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FServer-sent_events",[2839,2758],"\u002Fglossary#server-sent-events",{"slug":2933,"term":3202,"definition":3203,"category":2751,"aliases":3204,"links":3207,"related":3210,"readMore":-1,"target":3211,"hasArticle":2704},"Server-side rendering","Building a page as finished HTML on the server, so the first response already carries the content, headings, meta tags and structured data. Crawlers, link previews and slow devices read it without running JavaScript.",[3205,3206],"SSR","server-rendered",[3208],{"kind":2727,"url":3209},"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FServer-side_scripting",[2923],"\u002Fglossary#server-side-rendering",{"slug":2954,"term":3213,"definition":3214,"category":2695,"aliases":3215,"links":3218,"related":3223,"readMore":-1,"target":3224,"hasArticle":2704},"Skia","The open-source 2D graphics library from Google that draws Chrome, Android and — until Impeller — every Flutter frame. It renders to PDF as well as to a screen, which is what print-to-PDF in Chrome is doing.",[3216,3217],"Skia Graphics Engine","skia-safe",[3219,3221],{"kind":2700,"url":3220},"https:\u002F\u002Fskia.org\u002F",{"kind":2727,"url":3222},"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FSkia_Graphics_Engine",[2946],"\u002Fglossary#skia",{"slug":3226,"term":3227,"definition":3228,"category":2960,"aliases":3229,"links":3233,"related":3234,"readMore":-1,"target":3236,"hasArticle":2704},"staff-augmentation","Staff augmentation","A hiring model where engineers from an outside partner join your team and work under your management — in your repository, your sprints, your process — instead of delivering a project of their own. You buy capacity; the code and the context stay with you.",[3230,3231,3232],"team augmentation","dedicated developers","outstaffing",[],[3235,3121],"total-cost-of-ownership","\u002Fglossary#staff-augmentation",{"slug":2855,"term":3238,"definition":3239,"category":2737,"aliases":3240,"links":3243,"related":3244,"readMore":-1,"target":3245,"hasArticle":2704},"Staged rollout","Releasing a build to a small percentage of users first and widening only once the crash-free rate holds. A bad build caught at ten percent is a bad afternoon; the same build at a hundred percent is a bad week.",[3241,3242],"phased release","canary release",[],[2844,2734],"\u002Fglossary#staged-rollout",{"slug":3247,"term":2199,"definition":3248,"category":2751,"aliases":3249,"links":3253,"related":3256,"readMore":-1,"target":3257,"hasArticle":2704},"state-management","How an app decides where a value lives, who is allowed to change it, and which parts of the screen redraw when it does. In Flutter the choice between Riverpod, BLoC and Provider is among the first architectural decisions and the hardest to revisit.",[3250,3251,3252],"state management","BLoC","Riverpod",[3254],{"kind":2712,"url":3255},"https:\u002F\u002Fdocs.flutter.dev\u002Fdata-and-backend\u002Fstate-mgmt\u002Foptions",[2889],"\u002Fglossary#state-management",{"slug":2827,"term":3259,"definition":3260,"category":2983,"aliases":3261,"links":3265,"related":3268,"readMore":-1,"target":3269,"hasArticle":2704},"TLS","The encryption layer underneath HTTPS. It proves the server is who its certificate says, agrees a fresh key for the session, and encrypts everything after that — so the network in between sees ciphertext it cannot quietly alter.",[3262,3263,3264],"SSL","HTTPS","Transport Layer Security",[3266],{"kind":2727,"url":3267},"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FTransport_Layer_Security",[2817,2828],"\u002Fglossary#tls",{"slug":3121,"term":3271,"definition":3272,"category":2960,"aliases":3273,"links":3275,"related":3278,"readMore":-1,"target":3279,"hasArticle":2704},"Time to market","How long it takes to get a product from decision to real users. Most stack and scope arguments are really arguments about this number, because every week saved is a week of revenue, feedback and competitive position.",[3274,3121],"TTM",[3276],{"kind":2727,"url":3277},"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FTime_to_market",[3014,3235],"\u002Fglossary#time-to-market",{"slug":3235,"term":3281,"definition":3282,"category":2960,"aliases":3283,"links":3286,"related":3289,"readMore":-1,"target":3290,"hasArticle":2704},"Total cost of ownership","What a product costs across its whole life rather than to build once: maintenance, upgrades, annual OS and store migrations, and the second team you staff to keep two codebases in step. Usually larger than the build quote, and almost never inside it.",[3284,3285],"TCO","cost of ownership",[3287],{"kind":2727,"url":3288},"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FTotal_cost_of_ownership",[3121],"\u002Fglossary#total-cost-of-ownership",{"slug":3292,"term":2207,"definition":3293,"category":2983,"aliases":3294,"links":3296,"related":3301,"readMore":-1,"target":3303,"hasArticle":2704},"webrtc","The browser and mobile standard for sending audio, video and data directly between two devices, with servers involved only in introducing them to each other. It is what an in-app video call is built on when it is not a rented SDK.",[3295],"Web Real-Time Communication",[3297,3299],{"kind":2700,"url":3298},"https:\u002F\u002Fwebrtc.org\u002F",{"kind":2727,"url":3300},"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FWebRTC",[3302,2839],"xmpp","\u002Fglossary#webrtc",{"slug":2839,"term":3305,"definition":3306,"category":2983,"aliases":3307,"links":3308,"related":3311,"readMore":-1,"target":3312,"hasArticle":2704},"WebSocket","A protocol that holds one connection open between client and server so either side can send at any moment, instead of the client asking over and over. What live prices, chat and presence indicators run on.",[],[3309],{"kind":2727,"url":3310},"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FWebSocket",[2841,2758],"\u002Fglossary#websocket",{"slug":2856,"term":3314,"definition":3315,"category":2960,"aliases":3316,"links":3319,"related":3322,"readMore":3323,"target":3323,"hasArticle":804},"White-label","One product shipped under many brands. A white-label mobile platform builds each client a store-ready app with its own name, design and content from a single shared codebase, instead of forking the project per customer.",[3317,3318],"white label","multi-tenant app",[3320],{"kind":2727,"url":3321},"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FWhite-label_product",[3014],"\u002Fglossary\u002Fwhite-label",{"slug":3302,"term":3325,"definition":3326,"category":2983,"aliases":3327,"links":3330,"related":3335,"readMore":-1,"target":3336,"hasArticle":2704},"XMPP","An open, federated messaging protocol, and the long-standing alternative to writing a chat backend or renting one. It extends to presence, typing indicators and file transfer, and it is old enough that every platform has a mature client library.",[3328,3329],"Jabber","Extensible Messaging and Presence Protocol",[3331,3333],{"kind":2700,"url":3332},"https:\u002F\u002Fxmpp.org\u002F",{"kind":2727,"url":3334},"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FXMPP",[3292,2839],"\u002Fglossary#xmpp"]