<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Mikalai Siliuk</title>
    <description>The latest articles on DEV Community by Mikalai Siliuk (@mikalaisiliuk).</description>
    <link>https://dev.to/mikalaisiliuk</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4147083%2Fa2f31fbc-2289-4986-8bac-fc876014f7ff.jpg</url>
      <title>DEV Community: Mikalai Siliuk</title>
      <link>https://dev.to/mikalaisiliuk</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/mikalaisiliuk"/>
    <language>en</language>
    <item>
      <title>Mobile Analytics Event Naming: The Day-One Taxonomy Founders Skip</title>
      <dc:creator>Mikalai Siliuk</dc:creator>
      <pubDate>Mon, 19 Oct 2026 08:15:24 +0000</pubDate>
      <link>https://dev.to/mikalaisiliuk/mobile-analytics-event-naming-the-day-one-taxonomy-founders-skip-33ko</link>
      <guid>https://dev.to/mikalaisiliuk/mobile-analytics-event-naming-the-day-one-taxonomy-founders-skip-33ko</guid>
      <description>&lt;p&gt;Six months in, you sit down with the coffee and the CEO's Monday-morning question: why do users churn at the paywall? You open the dashboard. It shows three events — &lt;code&gt;paywall_view&lt;/code&gt;, &lt;code&gt;paywall_shown&lt;/code&gt;, and &lt;code&gt;paywallImpression&lt;/code&gt; — each firing from a different screen file, none of them lining up across iOS and Android, half of them firing twice. The funnel is unaskable. The reason is not the vendor and not the engineer. The reason is that nobody spent the two hours in week one on mobile analytics event naming, and every rename now costs a dashboard rebuild.&lt;/p&gt;

&lt;p&gt;This piece is a drill-down into one area of the eight-area mobile app architecture map — mobile analytics event naming, and only that area. If you want the full map first, start with &lt;a href="https://msappdev.com/blog/mobile-mvp-architecture-for-non-engineers/" rel="noopener noreferrer"&gt;mobile app architecture for founders&lt;/a&gt;; this article is the deep-dive that section 3 of that pillar forward-references. The sibling deep-dive on another day-one discipline is &lt;a href="https://msappdev.com/blog/payment-provider-abstraction-day-one/" rel="noopener noreferrer"&gt;payment provider abstraction from day one&lt;/a&gt;; the same "one file, one convention, enforced everywhere" pattern applies to both.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. What a mobile analytics event naming discipline actually is
&lt;/h2&gt;

&lt;p&gt;A mobile app analytics event taxonomy is one file. Usually called &lt;code&gt;analytics_events.ts&lt;/code&gt;, &lt;code&gt;analytics_events.dart&lt;/code&gt;, or &lt;code&gt;AnalyticsEvent.swift&lt;/code&gt; — the name does not matter. What matters is that this file declares every event the app will ever fire as a typed named constant, and no other file in the codebase invents an event string inline. Every &lt;code&gt;track(...)&lt;/code&gt; or &lt;code&gt;logEvent(...)&lt;/code&gt; call imports its event name from this file. That is the invariant. Break it in one screen and you have started accruing rename tax.&lt;/p&gt;

&lt;p&gt;How many analytics events mobile mvp needs is a smaller number than most founders expect. Ten to fifteen. The MVP-shaped set looks something like &lt;code&gt;install&lt;/code&gt;, &lt;code&gt;sign_up_start&lt;/code&gt;, &lt;code&gt;sign_up_complete&lt;/code&gt;, &lt;code&gt;first_key_feature_use&lt;/code&gt;, &lt;code&gt;paywall_shown&lt;/code&gt;, &lt;code&gt;paywall_accepted&lt;/code&gt;, &lt;code&gt;subscription_started&lt;/code&gt;, &lt;code&gt;subscription_renewed&lt;/code&gt;, &lt;code&gt;subscription_cancelled&lt;/code&gt;, and &lt;code&gt;retention_day_7&lt;/code&gt;. More than fifteen and you cannot hold the funnel in your head; fewer than eight and you cannot see between the steps. The count is a discipline, not a limit.&lt;/p&gt;

&lt;p&gt;The event naming convention mobile app teams pick matters less than that a convention exists and is enforced. Snake case, verb-noun order, past-tense: pick one, write it in a comment at the top of the file, and reject the pull request that violates it. &lt;code&gt;paywall_shown&lt;/code&gt; beats &lt;code&gt;showedPaywall&lt;/code&gt; — but &lt;code&gt;showedPaywall&lt;/code&gt; used consistently across every event beats &lt;code&gt;paywall_shown&lt;/code&gt; mixed with &lt;code&gt;paywallImpression&lt;/code&gt; and &lt;code&gt;PaywallView&lt;/code&gt;. Consistency is the discipline; the exact convention is folklore.&lt;/p&gt;

&lt;p&gt;The other half of the file is the analytics events schema mobile mvp will be reasoning about a year from now. The event &lt;code&gt;paywall_shown&lt;/code&gt; is not just a name — it carries &lt;code&gt;paywall_variant&lt;/code&gt;, &lt;code&gt;product_id&lt;/code&gt;, and &lt;code&gt;entry_point&lt;/code&gt;. Those three properties are declared alongside the event constant with types, and any call site that tracks the event without them is a compile error. This is the event properties naming mobile analytics half of the discipline that the "just track a bunch of events" pitch skips. Without &lt;code&gt;paywall_variant&lt;/code&gt;, "which paywall converted better?" is unanswerable, no matter which vendor you pay.&lt;/p&gt;

&lt;p&gt;Written together, the file is the analytics events single source of truth for the whole app. Ten to fifteen typed constants, a properties schema per event, one comment at the top explaining the naming convention. That is mobile analytics event naming as a discipline, in two hours of typing.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Paying for the best vendor is orthogonal to mobile analytics event naming
&lt;/h2&gt;

&lt;p&gt;This is the counter-intuitive point most non-technical founders get wrong. Paying $500 a month for Amplitude does not mean you have an event taxonomy. Amplitude — or Mixpanel, PostHog, or Firebase Analytics, treat all four as interchangeable here — is a very good implementation of the dashboard, funnel builder, and cohort explorer. It is the pipe and the visualisation. It is not the naming discipline. The vendor cannot reach up into your codebase and rename the inline event strings your engineers wrote last month. Only the taxonomy file can, because only the taxonomy file has one copy of each name.&lt;/p&gt;

&lt;p&gt;Founders skip this decision because the engineer said "Amplitude handles the analytics for us" — which is true for storage, funnels, and dashboards, and irrelevant to whether &lt;code&gt;paywall_view&lt;/code&gt; (fired from &lt;code&gt;PaywallScreen&lt;/code&gt;), &lt;code&gt;paywall_shown&lt;/code&gt; (fired from an A/B test wrapper), and &lt;code&gt;paywallImpression&lt;/code&gt; (fired from a shared header component that happens to render on the paywall screen) all end up in the same project, competing for the same reality. The &lt;code&gt;paywall_shown vs paywall_view&lt;/code&gt; argument is not resolvable at the vendor. It is only resolvable at the file that says "one of these is the event; the other two do not exist."&lt;/p&gt;

&lt;p&gt;Think of it in three layers. Layer 1 is the callers — screens and business logic. Layer 2 is the taxonomy file — mobile analytics event naming as a single source of truth. Layer 3 is the vendor SDK — the pipe and the dashboard. Skipping Layer 2 collapses Layer 1 straight into Layer 3: every screen calls the vendor SDK with a stringly-typed event name of its author's choice. Renaming analytics events mobile teams need to change later — because the growth team asked for dashboard clarity, or because a year-two analytics hire cannot make sense of the funnel — turns into a codebase-wide grep, a scattergun search-and-replace, and a lost month of comparability when historical events do not match the renamed ones.&lt;/p&gt;

&lt;p&gt;The discipline generalises. Identity events (&lt;code&gt;sign_up_complete&lt;/code&gt;, &lt;code&gt;login_success&lt;/code&gt;) live in the same taxonomy and follow the same convention — see &lt;a href="https://msappdev.com/blog/auth-identity-decisions-haunt-mvp/" rel="noopener noreferrer"&gt;the auth and identity decisions that haunt your mobile MVP&lt;/a&gt; for the identity-model discussion those events sit on top of.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Three founder-side questions before the first &lt;code&gt;paywall_shown&lt;/code&gt; ships
&lt;/h2&gt;

&lt;p&gt;Mobile analytics event naming is easy to verify without reading a line of code. Ask three questions in the architectural review and listen to the shape of the answer.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Question 1: "Show me the one file that lists every event the app fires and the naming convention behind it."&lt;/strong&gt; If the engineer opens &lt;code&gt;PaywallScreen.tsx&lt;/code&gt; and points at inline &lt;code&gt;track('paywall_view', {...})&lt;/code&gt; strings scattered across the screen, the taxonomy does not exist and you are watching the failure mode being built in real time. If they open &lt;code&gt;analytics_events.ts&lt;/code&gt;, it declares every event as a typed constant with the properties schema alongside, and the paywall screen imports &lt;code&gt;AnalyticsEvents.PAYWALL_SHOWN&lt;/code&gt; from that file, the taxonomy exists. The file name is not the point; the single answer — "here is the one file, and every &lt;code&gt;track&lt;/code&gt; call imports from it" — is.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Question 2: "If I asked you tomorrow to rename &lt;code&gt;paywall_view&lt;/code&gt; to &lt;code&gt;paywall_shown&lt;/code&gt; because the growth team wants dashboard clarity, what would change?"&lt;/strong&gt; The right answer names one file plus a one-line note in the vendor dashboard to alias the historical event so retention curves stay comparable. That is a one-hour change. The wrong answer describes grepping the codebase for &lt;code&gt;paywall_view&lt;/code&gt;, checking every screen, hoping we caught the ones that got misspelled, then a QA pass to verify nothing else broke. That answer is the rename-tax quote your engineer will present in month six. Ask it in week one instead — you do not need to read the code, you need to hear the shape of the answer.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Question 3: "Where is the properties schema — how do we know every &lt;code&gt;paywall_shown&lt;/code&gt; event carries &lt;code&gt;paywall_variant&lt;/code&gt; and &lt;code&gt;product_id&lt;/code&gt;, and that no event fires without them?"&lt;/strong&gt; The right answer is the taxonomy file itself (typed properties on the event constant so it is a compile error to omit them) plus a small runtime assertion in the analytics wrapper as a belt-and-braces check. The wrong answer is "the engineer remembers to pass them." The wrong answer ships fine for the first hundred events; it produces the "why does 40% of &lt;code&gt;paywall_shown&lt;/code&gt; have a null &lt;code&gt;paywall_variant&lt;/code&gt;" support-thread in month four.&lt;/p&gt;

&lt;p&gt;I have cleaned up codebases where the paywall fired three events — &lt;code&gt;paywall_view&lt;/code&gt;, &lt;code&gt;paywall_shown&lt;/code&gt;, and &lt;code&gt;paywallImpression&lt;/code&gt; — from three different files, each written by whichever engineer was on the screen that week. The funnel showed a 300% conversion rate because the vendor was counting all three as separate impressions and matching all three against the same purchase. Rebuilding every funnel to use one canonical event name meant a dashboard rebuild, a re-learned baseline, and a lost month of comparability. The engineer who wrote the codebase held the same title, used the same vendor, and delivered on time. The difference between that codebase and one that survives the month-six funnel review is one file of mobile analytics event naming that was never written.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Where AI-augmented delivery lands
&lt;/h2&gt;

&lt;p&gt;AI writes the vendor SDK wiring in minutes — the initialisation, the &lt;code&gt;identify&lt;/code&gt; call, the &lt;code&gt;track&lt;/code&gt; boilerplate, the offline queue, the flush-on-background handling. What AI does not do is decide which ten to fifteen events carry every funnel report the founder actually needs, what to name them so they still make sense to a data analyst who joins in month twelve, or which properties to attach so the paywall-variant question is answerable a year from now. That judgement is what the senior engineer is paid for. See &lt;a href="https://msappdev.com/ai-delivery/" rel="noopener noreferrer"&gt;how AI-augmented delivery works in practice&lt;/a&gt; for the workflow.&lt;/p&gt;

&lt;p&gt;The positive proof is on the wall. &lt;a href="https://msappdev.com/projects/boyfi/" rel="noopener noreferrer"&gt;Boyfi&lt;/a&gt; runs two monetisation surfaces — App Store IAP through Adapty and a web funnel through a custom Stripe API — and the funnel report reconciles both channels because mobile analytics event naming was locked once, in one file, before any instrumentation shipped. The same &lt;code&gt;subscription_started&lt;/code&gt; fires from mobile and from web with the same properties, and the cross-channel funnel is answerable without a reconciliation script. &lt;a href="https://msappdev.com/projects/mycoach/" rel="noopener noreferrer"&gt;MyCoach&lt;/a&gt; has run the same taxonomy file for two years and sixty-plus updates; the workout-completion funnel is comparable across the entire product lifetime. &lt;a href="https://msappdev.com/projects/hairly/" rel="noopener noreferrer"&gt;Hairly&lt;/a&gt; declares &lt;code&gt;generation_started&lt;/code&gt;, &lt;code&gt;generation_completed&lt;/code&gt;, and &lt;code&gt;generation_failed&lt;/code&gt; with a &lt;code&gt;provider&lt;/code&gt; property attached — three different image providers have been added by writing one adapter each, and the analytics side has needed zero changes.&lt;/p&gt;

&lt;h2&gt;
  
  
  The one file that saves the month-six funnel
&lt;/h2&gt;

&lt;p&gt;The taxonomy is two hours in week one. The rename tax is a month in year one. Everything else is the same — the same vendor, the same engineer, the same product — and the difference is whether ten to fifteen event names live in one file with a property schema, or in twelve screen files with whatever the author was thinking that afternoon.&lt;/p&gt;

&lt;p&gt;A clean taxonomy also pays off later in a way that was not obvious when this was written: once the names are consistent, an AI assistant can query the stack directly and be trusted with the answer. That is the subject of &lt;a href="https://msappdev.com/blog/mcp-mobile-app-analytics/" rel="noopener noreferrer"&gt;MCP for mobile app analytics&lt;/a&gt;, a live case study on a 1,000-DAU app. Garbage names in, three contradictory answers out.&lt;/p&gt;

&lt;p&gt;If you are about to commission a mobile MVP, ask the three questions above before the first paywall instrumentation ships. If the answers do not sound like "here is the file," the codebase is already accruing rename tax. &lt;a href="https://msappdev.com/#contact" rel="noopener noreferrer"&gt;Tell me about your project&lt;/a&gt; and I will tell you whether mobile analytics event naming is already implied by your scope or the decision your engineer is about to skip. Or see &lt;a href="https://msappdev.com/services/" rel="noopener noreferrer"&gt;the engagement model&lt;/a&gt; where analytics — and the other seven architectural areas — are owned end-to-end.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="https://msappdev.com/blog/analytics-event-taxonomy-mobile/?utm_source=devto&amp;amp;utm_medium=social&amp;amp;utm_campaign=analytics-event-taxonomy-mobile" rel="noopener noreferrer"&gt;msappdev.com&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>analytics</category>
      <category>architecture</category>
      <category>mvp</category>
    </item>
    <item>
      <title>The Mobile App Authentication Choices That Haunt Your MVP</title>
      <dc:creator>Mikalai Siliuk</dc:creator>
      <pubDate>Mon, 12 Oct 2026 08:39:10 +0000</pubDate>
      <link>https://dev.to/mikalaisiliuk/the-mobile-app-authentication-choices-that-haunt-your-mvp-423p</link>
      <guid>https://dev.to/mikalaisiliuk/the-mobile-app-authentication-choices-that-haunt-your-mvp-423p</guid>
      <description>&lt;p&gt;Most mobile app authentication choices get twenty minutes in week one. A minority get the room. The two look identical in the demo. They diverge on a Monday morning in month eleven — when a growth channel needs phone-based reactivation and your entire user base has no phone number attached, when Apple rejects the build because the app shipped Google Sign-In without Sign in with Apple, or when a paying user opens the app after a refresh-token expiry and finds themselves silently logged out of the subscription they paid for.&lt;/p&gt;

&lt;p&gt;That is what the mobile app authentication choices you make in week one actually determine. Not "which button appears on the login screen." Which of five sub-decisions your engineer took together, and which they quietly skipped. The eight-area map &lt;a href="https://msappdev.com/blog/mobile-mvp-architecture-for-non-engineers/" rel="noopener noreferrer"&gt;names auth as Area 1 and points here for the drill-down&lt;/a&gt;. If you have not yet accepted that auth belongs at the top of the map, &lt;a href="https://msappdev.com/blog/non-technical-founder-mobile-architecture/" rel="noopener noreferrer"&gt;the Foundations pillar for non-technical founders&lt;/a&gt; is the earlier stop.&lt;/p&gt;

&lt;p&gt;The single-sentence argument: mobile app authentication choices are not "Firebase Auth or Supabase Auth." They are a stack of five sub-decisions taken together, and an MVP that shipped three of the five and defaulted the other two is buying a month-eleven migration.&lt;/p&gt;

&lt;h2&gt;
  
  
  Login methods: the five options — and the one you actually need in week 1
&lt;/h2&gt;

&lt;p&gt;The first of the mobile app authentication choices is which login mechanism your app exposes to the user in week one. The real options in 2026 collapse to five, plus one modifier:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Email + password.&lt;/strong&gt; Familiar, universal, no vendor lock-in. Costs you password-reset flows and one support-ticket surface per user who forgets.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Email magic link.&lt;/strong&gt; Passwordless — the user taps a link in their inbox. Better UX; needs reliable transactional email and a fallback for users whose inbox filters the link into spam.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SMS OTP&lt;/strong&gt; (one-time password — a numeric code sent by text). Faster than email for many users; more expensive per sign-up; regulatorily fussy in some countries.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Social sign-in.&lt;/strong&gt; Google, Facebook, Apple. Fastest onboarding by a wide margin. Ties your identity to a third-party account you do not control.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Passkeys.&lt;/strong&gt; The 2026 addition. Native on iOS 17+ and Android 14+. Passwordless and phishing-resistant. The passkeys vs sms otp mobile app comparison lands differently than a year ago — passkeys have crossed the "mainstream enough to consider" threshold, though shipping day-one is still a judgement call.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Plus one modifier — &lt;strong&gt;anonymous auth&lt;/strong&gt; — which is not really a login method. It gives the user a session identity without asking for anything and lets your app open a database record for them. That is what makes anonymous auth mobile mvp shortcuts tempting. The trap is not anonymous auth. The trap is anonymous-only auth with no upgrade path.&lt;/p&gt;

&lt;p&gt;The rule: pick &lt;strong&gt;one&lt;/strong&gt; primary identifier for week one, and design the anonymous-linking flow next to it. "We'll add the other methods later" is only cheap if the &lt;em&gt;upgrade path&lt;/em&gt; was designed alongside the primary identifier in week one.&lt;/p&gt;

&lt;h3&gt;
  
  
  The App Store mandate: Sign in with Apple is not optional
&lt;/h3&gt;

&lt;p&gt;If your app offers any third-party social sign-in — Google, Facebook, X, whatever — Sign in with Apple must be offered alongside. This is App Store Review Guideline 4.8 and it is enforced consistently. The sign in with apple mobile mvp check is the single most common auth-related rejection I see when I clean up failed submissions.&lt;/p&gt;

&lt;p&gt;Founders miss it because their engineer scoped "add Google sign-in" as a one-week task and never mentioned Apple. Cost pre-submission: one entitlement, one login button. Cost post-rejection: one week of scramble, one review re-queue, one unhappy investor asking why the app is not live yet. The sign in with apple vs google mobile mvp choice is not "either/or" for any app shipping social login — it is "and."&lt;/p&gt;

&lt;h2&gt;
  
  
  Provider choice: the framework that will host your user model for three years
&lt;/h2&gt;

&lt;p&gt;The second of the mobile app authentication choices is provider. Whichever your engineer picks becomes the primary key on every user record and the API you build every downstream auth flow against. Migrating providers means every user's identifier changes — you either force reauthentication (expect double-digit percentage attrition) or build a shadow mapping table and pay for it in every future query.&lt;/p&gt;

&lt;p&gt;The five real options — the mobile app auth provider comparison you can hold in your head:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Firebase Auth.&lt;/strong&gt; The default for Flutter and Firebase-backed MVPs. Anonymous sign-in built in. Broad social provider list. Ties you to Google Cloud pricing. Free tier covers most MVPs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Supabase Auth.&lt;/strong&gt; The default for Postgres-backed MVPs. Row-level security integrates cleanly with the auth schema. The firebase auth vs supabase mobile mvp trade-off is usually decided by the rest of your backend stack, not by the auth layer alone.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Auth0.&lt;/strong&gt; Enterprise-grade and priced for it. Justifies its cost when you need SAML, multi-tenancy, or granular RBAC. When to add sso to mobile mvp: the day a B2B customer asks for it, not before.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Clerk.&lt;/strong&gt; React-first, excellent DX on the web. Mobile SDK story is improving but Flutter-first shops still default elsewhere.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Custom.&lt;/strong&gt; Only when the other four cannot host a required identity model — a regulated jurisdiction with local key-storage mandates, or a partnership where your customer's own SSO is the identity source.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I built &lt;a href="https://msappdev.com/projects/boyfi/" rel="noopener noreferrer"&gt;Boyfi&lt;/a&gt; on Firebase Auth with anonymous-first sign-in that upgrades cleanly when the user commits to the paid tier — critical because Boyfi runs a parallel Stripe-powered web funnel and the identity has to bridge both. Eight months in, no user has been told "your purchase history is gone because you signed in." That is what the anonymous-to-identified upgrade buys when designed in week one. On &lt;a href="https://msappdev.com/projects/mycoach/" rel="noopener noreferrer"&gt;MyCoach&lt;/a&gt;, the same Firebase Auth choice has hosted the user model for two years and sixty-plus updates without being touched — the whole workout-history data model hangs off a stable primary key. Pick-one-and-commit is what lets a codebase run two years without an auth migration ever showing up in the sprint.&lt;/p&gt;

&lt;p&gt;Worth naming: AI writes the Firebase Auth or Supabase Auth SDK wiring in minutes now. It does not decide whether your identity model should be email-first, phone-first, or anonymous-first — the migration cost of getting that wrong is not covered by the AI's speed. That is what a senior engineer's judgement is paid for. See &lt;a href="https://msappdev.com/ai-delivery/" rel="noopener noreferrer"&gt;how AI-augmented delivery works in practice&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The three auth decisions that show up in month eleven
&lt;/h2&gt;

&lt;p&gt;The three sub-decisions inside the mobile app authentication choices that most reliably produce the year-two migration the architecture pillar warned about. Each with the founder-side test: what to ask the engineer to &lt;em&gt;show&lt;/em&gt;, not describe.&lt;/p&gt;

&lt;h3&gt;
  
  
  Decision A: the anonymous-to-identified upgrade path
&lt;/h3&gt;

&lt;p&gt;I have cleaned up codebases where the founder rubber-stamped anonymous-only sign-in in week one because it sounded simple, and hit month eleven needing phone-based reactivation. The entire user base had no phone number attached. The migration path was "email your existing users a link and hope they log in again." A meaningful percentage did not. Anonymous auth mobile mvp shortcuts are not the trap. Anonymous auth with no upgrade path is.&lt;/p&gt;

&lt;p&gt;The pattern that saves this is the anonymous to identified user migration mobile flow: when the anonymous user commits (paywall, feature-gate, whatever the trigger is), the app upgrades the account to email, phone, or social identity while preserving the same underlying record — same primary key, same purchase history, same analytics timeline. Firebase Auth exposes this via &lt;code&gt;linkWithCredential&lt;/code&gt;; Supabase and Auth0 have equivalents. What matters is that the engineer designed the flow in week one.&lt;/p&gt;

&lt;p&gt;Founder-side test: ask the engineer to &lt;em&gt;show&lt;/em&gt; the upgrade running on a device. Not describe it. Show. If it does not exist yet, ask them to draw the sequence diagram on the spot.&lt;/p&gt;

&lt;h3&gt;
  
  
  Decision B: Sign in with Apple, done before the first submission
&lt;/h3&gt;

&lt;p&gt;Restated from Section 1 because it belongs on the "show me" list. If your app ships any social login and the Sign in with Apple button is not on the login screen at submission, expect rejection. Founder-side test: ask to see the login screen on TestFlight before submission.&lt;/p&gt;

&lt;h3&gt;
  
  
  Decision C: session and refresh-token behaviour
&lt;/h3&gt;

&lt;p&gt;The least visible of the mobile app authentication choices and the most common cause of "why did the app log me out?" support tickets. Sessions expire. Refresh tokens rotate. If the app does not handle refresh silently in the background, the user opens the app on a Monday morning, gets kicked to the login screen, and — if their subscription is tied to that session — cannot find their entitlement.&lt;/p&gt;

&lt;p&gt;The choices to make: how long a session lasts, how the refresh token rotates, what happens when refresh itself expires, and how the app reconnects entitlement state after recovery. None of these are exotic. All are skipped by MVPs that assumed the provider's defaults were "obviously fine." Founder-side test: leave the app for a week, come back. If you land on the paywall and not on your paid content, the session strategy was never designed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Five questions before you sign off on auth
&lt;/h2&gt;

&lt;p&gt;Take these into your architectural review. If the engineer cannot answer one, that is the sub-decision they were going to skip.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Which login method is primary in week one, and which anonymous-linked upgrade path connects to it?&lt;/li&gt;
&lt;li&gt;Which identity provider hosts the user model, and where is the lock-in if we ever swap?&lt;/li&gt;
&lt;li&gt;Is Sign in with Apple in the build before we submit, given any third-party social provider we ship?&lt;/li&gt;
&lt;li&gt;How does an anonymous user upgrade to identified without losing purchase history?&lt;/li&gt;
&lt;li&gt;What is the session lifetime, how does refresh rotate, and what happens when refresh expires?&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;These are the mobile app authentication choices that decide whether month eleven brings a graceful growth-channel launch or a migration Slack thread.&lt;/p&gt;

&lt;h2&gt;
  
  
  The engineer who cannot show the sequence has not made the decision
&lt;/h2&gt;

&lt;p&gt;If any of the five above gets a description instead of a demo — "we'll handle refresh with the default," "we can add Apple later," "anonymous upgrade is easy" — that is the sub-decision that is still open. Open sub-decisions are what the year-two rewrite is made of. The pillar told you auth is Area 1 and migrations are lossy. This article told you which five sub-decisions inside the mobile app authentication choices cost the migration when they get skipped.&lt;/p&gt;

&lt;p&gt;If you want someone to walk your scope against the five before you sign, &lt;a href="https://msappdev.com/#contact" rel="noopener noreferrer"&gt;tell me about your project&lt;/a&gt; — I will tell you which your scope already answers, and which your engineer has been quietly deferring. The engagement model where auth and the other seven architectural areas are owned end-to-end is on the &lt;a href="https://msappdev.com/services/" rel="noopener noreferrer"&gt;Mobile MVP Development service page&lt;/a&gt;.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="https://msappdev.com/blog/auth-identity-decisions-haunt-mvp/?utm_source=devto&amp;amp;utm_medium=social&amp;amp;utm_campaign=auth-identity-decisions-haunt-mvp" rel="noopener noreferrer"&gt;msappdev.com&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>auth</category>
      <category>architecture</category>
      <category>mvp</category>
    </item>
    <item>
      <title>Flutter State Management in 2026: What Founders Should Know Before Their Team Picks</title>
      <dc:creator>Mikalai Siliuk</dc:creator>
      <pubDate>Mon, 05 Oct 2026 19:28:01 +0000</pubDate>
      <link>https://dev.to/mikalaisiliuk/flutter-state-management-in-2026-what-founders-should-know-before-their-team-picks-2dog</link>
      <guid>https://dev.to/mikalaisiliuk/flutter-state-management-in-2026-what-founders-should-know-before-their-team-picks-2dog</guid>
      <description>&lt;p&gt;The flutter state management 2026 conversation is a religious war, and like most religious wars it distracts from the decision that actually matters. Founders keep asking which library their engineer should pick. The engineer, in turn, keeps defending whichever pattern they last used in production. Both sides are answering the wrong question. In flutter state management 2026, the library choice barely moves the outcome. What determines whether the codebase is still readable in month eighteen is whether the team picked one pattern in week one and held it in every screen for the next twelve months.&lt;/p&gt;

&lt;p&gt;This article is a founder-facing decision guide, not a library benchmark. If your engineer is about to pick a Flutter state management library you have never heard of, and you do not know how to tell whether the choice is defensible, this is the frame that lets you evaluate them without reading a line of Dart.&lt;/p&gt;

&lt;h2&gt;
  
  
  The 2026 short list, and why "which one?" is the wrong founder question
&lt;/h2&gt;

&lt;p&gt;The flutter state management 2026 short list has finally narrowed. Riverpod 2.x, BLoC, Provider, and the newer signals-based approaches (Solidart, the &lt;code&gt;signals&lt;/code&gt; package, and the wider fine-grained-reactivity ecosystem) are all defensible. Any of them can ship a maintainable Flutter codebase. None of them can save one where each screen uses a different pattern.&lt;/p&gt;

&lt;p&gt;That last sentence is the one founders miss. The library debate produces a confident engineer answer. The consistency question produces a shrug. Which is exactly backwards.&lt;/p&gt;

&lt;p&gt;When a founder asks "which Flutter state management should I use in 2026?", the answer that separates a senior engineer from a mid-level one sounds like this: any of the four, but the one you already picked. A senior Flutter engineer walks into a codebase, reads five files, sees which pattern is in use, and defaults to that pattern for the next feature. A mid-level engineer walks in with their favourite framework and starts writing the sixth flavour. This is the single most important flutter state management pattern for startups: the pattern that is already in your codebase.&lt;/p&gt;

&lt;p&gt;For the upstream framework decision that has to happen before this one, see &lt;a href="https://msappdev.com/blog/flutter-vs-native-startup-mvp/" rel="noopener noreferrer"&gt;Flutter vs native for startup MVPs&lt;/a&gt;. That article settles whether you are on Flutter at all. This one assumes you are, and picks up where the state layer decision begins.&lt;/p&gt;

&lt;h2&gt;
  
  
  What actually changed in the last eighteen months
&lt;/h2&gt;

&lt;p&gt;The flutter state management 2026 landscape is meaningfully different from the 2022 one. Three things shifted, and they matter for the founder-side decision.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Riverpod 2.x stabilised.&lt;/strong&gt; The code-generation-first workflow (&lt;code&gt;@riverpod&lt;/code&gt; annotation, &lt;code&gt;AsyncNotifier&lt;/code&gt;, &lt;code&gt;Notifier&lt;/code&gt;, generated &lt;code&gt;.g.dart&lt;/code&gt; files) is now the mainstream Riverpod. The older &lt;code&gt;StateProvider&lt;/code&gt; and &lt;code&gt;StateNotifierProvider&lt;/code&gt; API still works, but new codebases skip it. In the riverpod vs bloc for mvp question, Riverpod 2.x with code generation now typically wins on ergonomics for most consumer-app scopes: fewer files per feature, tighter type safety, less boilerplate around async data. BLoC still wins where the team is genuinely fluent in it already, or where the product is heavy on discrete event streams (video conferencing, live audio, complex form state machines).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Signals-based state matured.&lt;/strong&gt; The &lt;code&gt;signals&lt;/code&gt; package, alongside Solidart and the wider fine-grained reactivity ecosystem, moved from "interesting experiment" to "ships to App Store." The flutter signals vs riverpod comparison is now real: signals give you finer reactivity and less rebuild noise; Riverpod gives you a more opinionated dependency-injection model and a better async-data story. Both are defensible for a 2026 startup MVP. Neither is a mistake.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AI-assisted delivery exploded, and it broke consistency.&lt;/strong&gt; This is the shift most founders do not see. When Claude Code, Cursor, or Copilot generates a screen, the state pattern it picks depends on what was in its context window five minutes ago. A codebase that started clean can accumulate three or four state patterns within a month if nobody is reviewing for flutter state management consistency at the pull-request level. This is the anti-pattern I now see most often when I am called into someone else's Flutter codebase: six state patterns in one app, one per sprint, each introduced by a different AI session that had a different pattern in its context.&lt;/p&gt;

&lt;p&gt;For the categorical view of where state fits inside the wider architecture picture, see &lt;a href="https://msappdev.com/blog/mobile-mvp-architecture-for-non-engineers/" rel="noopener noreferrer"&gt;mobile app architecture for founders&lt;/a&gt;. This article is the deep dive that the state-management area of that pillar forwards to.&lt;/p&gt;

&lt;h2&gt;
  
  
  Pick-and-hold: the one-pattern rule
&lt;/h2&gt;

&lt;p&gt;The flutter state management 2026 rule that actually protects the codebase is boring: pick one pattern in week one, document a canonical example in the repo, and reject any pull request that introduces a second pattern without an architecture decision record (ADR).&lt;/p&gt;

&lt;p&gt;The one state pattern flutter codebase discipline is not a preference. It is what lets a new engineer read the canonical example, copy it, and produce a seventh feature that looks like the first six. When every screen follows the same shape, the codebase compounds. When each screen invents its own shape, the codebase becomes a tour of the industry's last eighteen months of state-management fashion, and the rewrite quote arrives around month fourteen.&lt;/p&gt;

&lt;p&gt;The proof this works is not theoretical. Take &lt;a href="https://msappdev.com/projects/mycoach/" rel="noopener noreferrer"&gt;MyCoach&lt;/a&gt;: one Flutter codebase, one state pattern, sixty-plus updates over two years, and the state layer has not been rewritten. Take &lt;a href="https://msappdev.com/projects/boyfi/" rel="noopener noreferrer"&gt;Boyfi&lt;/a&gt;: dual subscriptions running in parallel, two AI providers behind one abstraction, then a third AI provider added eight months in without the state layer moving. Take &lt;a href="https://msappdev.com/projects/hairly/" rel="noopener noreferrer"&gt;Hairly&lt;/a&gt;: three image providers grew to five, each new provider added as one adapter, the state pattern untouched. These are not proof that any specific library is correct. They are proof that pattern discipline compounds. And in flutter state management 2026, discipline is the whole game.&lt;/p&gt;

&lt;h2&gt;
  
  
  The consistency trap, and how AI-augmented delivery amplifies it
&lt;/h2&gt;

&lt;p&gt;In flutter state management 2026, the consistency failure mode arrives faster than it used to. I have been called into codebases where BLoC lived in the paywall, Provider in the settings, and Riverpod in the onboarding. Six months of feature work behind an eighteen-month rewrite quote. Each engineer defended their pattern as the right pattern for their module. Each was right in isolation. Each was wrong at the codebase level.&lt;/p&gt;

&lt;p&gt;The 2026 version of this is faster and quieter. AI-generated features can arrive in a pull request before a founder can even ask what state pattern is being introduced. If the reviewer is not enforcing flutter state management consistency at the diff level, the codebase silently becomes a Frankenstein within a quarter. The AI is not the problem. The absence of a canonical pattern reference in the repo is the problem. For the engagement model where AI-augmented delivery is bounded by a canonical pattern rather than freewheeling per session, see &lt;a href="https://msappdev.com/ai-delivery/" rel="noopener noreferrer"&gt;how AI-augmented delivery actually works&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The Flutter team's own position, roughly paraphrased from their state-management docs and recent conference talks, is that any pattern is fine and consistency is the discipline that matters. That is the correct answer, and it is also the boring answer, which is why it keeps losing to the exciting answer ("Riverpod is the future" / "Signals are the future" / "BLoC is enterprise-grade") every time a founder lets tribal preference set the direction.&lt;/p&gt;

&lt;h2&gt;
  
  
  The thirty-second founder test
&lt;/h2&gt;

&lt;p&gt;You do not need to read Dart to check whether the flutter state management 2026 discipline is real in your codebase. Ask the engineer two questions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Question one.&lt;/strong&gt; "Point me to one file in the repo that is the canonical example of our state pattern. If a new engineer joined today, which file would you tell them to copy?"&lt;/p&gt;

&lt;p&gt;If the answer is confident, specific, and takes under thirty seconds (something like "&lt;code&gt;lib/features/paywall/paywall_controller.dart&lt;/code&gt; is the canonical shape"), the discipline is real. If the answer is a monologue about how "it depends on the feature" or "we haven't fully standardised yet," the discipline is not real and the codebase is already accumulating patterns.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Question two.&lt;/strong&gt; "What state pattern will every new screen use for the next twelve months?"&lt;/p&gt;

&lt;p&gt;If the engineer names a pattern and can point to the ADR or README section that documents it, you are safe. If the answer is "we'll evaluate per feature," the eighteen-month rewrite is already scheduled. The right time to enforce the one state pattern flutter codebase rule is week one. The wrong time is month fourteen, when the pattern-per-sprint tax has already been paid.&lt;/p&gt;

&lt;h2&gt;
  
  
  The founder summary
&lt;/h2&gt;

&lt;p&gt;The flutter state management 2026 decision is boring on purpose. Riverpod 2.x, BLoC, Provider, signals: all four ship. The library your engineer picks matters less than whether they will still be using it in month twelve. In every codebase I have seen survive its second year, the flutter state management consistency discipline was in place from week one. In every codebase that hit the year-two rewrite quote, it was not.&lt;/p&gt;

&lt;p&gt;If you are about to commission a Flutter MVP and want the state pattern picked and defended before the first paywall ships, &lt;a href="https://msappdev.com/#contact" rel="noopener noreferrer"&gt;tell me about your project&lt;/a&gt;. If you want the engagement model where architecture is owned end-to-end and the canonical pattern is documented in week one, &lt;a href="https://msappdev.com/services/" rel="noopener noreferrer"&gt;see the service menu&lt;/a&gt;.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="https://msappdev.com/blog/flutter-state-management-2026/?utm_source=devto&amp;amp;utm_medium=social&amp;amp;utm_campaign=flutter-state-management-2026" rel="noopener noreferrer"&gt;msappdev.com&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>flutter</category>
      <category>statemanagement</category>
      <category>architecture</category>
    </item>
    <item>
      <title>Mobile Payment Provider Abstraction from Day One</title>
      <dc:creator>Mikalai Siliuk</dc:creator>
      <pubDate>Thu, 01 Oct 2026 06:18:45 +0000</pubDate>
      <link>https://dev.to/mikalaisiliuk/mobile-payment-provider-abstraction-from-day-one-4ifp</link>
      <guid>https://dev.to/mikalaisiliuk/mobile-payment-provider-abstraction-from-day-one-4ifp</guid>
      <description>&lt;p&gt;Build the wrapper before you have a paywall. Before paying users. Before you have finalised whether the paywall SDK behind it will be RevenueCat, Adapty, or Qonversion. That is the argument, and it is the exact opposite of what most non-technical founders hear from their engineer, which is usually a version of "we'll just use RevenueCat, it handles everything." Mobile payment provider abstraction is not what RevenueCat does. Mobile payment provider abstraction is what stands &lt;em&gt;between&lt;/em&gt; your app and RevenueCat — one file, four verbs, and a boundary that decides whether swapping paywall SDKs is a one-day adapter change or a quarter of touching every screen. The &lt;a href="https://msappdev.com/blog/mobile-mvp-architecture-for-non-engineers/" rel="noopener noreferrer"&gt;eight-area architectural map&lt;/a&gt; names payments as Area 2 and points here. This is the drill-down.&lt;/p&gt;

&lt;p&gt;The single-sentence thesis: a &lt;code&gt;PaymentService&lt;/code&gt; interface — one file that knows what the payment system needs to do (start a subscription, restore purchases, check entitlement, cancel) and hides which provider does it — is the single most valuable line of defence against the year-two subscription rewrite, and it belongs in the codebase before you have picked the SDK. The SDK is the implementation detail. The interface is the design decision. Get the interface wrong and the SDK choice will still look easy in the demo — and cost you a quarter of engineering the first time you need to change it.&lt;/p&gt;

&lt;h2&gt;
  
  
  What a mobile payment provider abstraction actually is — one file, four verbs, zero SDK types anywhere else
&lt;/h2&gt;

&lt;p&gt;The concrete shape is small. One file, usually called &lt;code&gt;PaymentService&lt;/code&gt; or &lt;code&gt;payment_service.dart&lt;/code&gt; or &lt;code&gt;PaymentService.swift&lt;/code&gt;, that exposes an interface with four verbs the app needs and hides everything else:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;startSubscription(productId)&lt;/code&gt;&lt;/strong&gt; — begin the purchase flow for a subscription product. Whatever the SDK requires (product identifier lookup, StoreKit or Play Billing handshakes, receipt collection) happens inside this call.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;restorePurchases()&lt;/code&gt;&lt;/strong&gt; — the App-Store-mandated flow that lets a returning user get their subscription back on a new device.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;checkEntitlement()&lt;/code&gt;&lt;/strong&gt; — returns whether the current user has access. Not "is there a purchase in local state" — whether the &lt;em&gt;server&lt;/em&gt; says the user is entitled. This is where server-side receipt validation mobile MVP correctness lives.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;cancel()&lt;/code&gt;&lt;/strong&gt; — opens the platform's cancellation surface. On iOS this is a deep link to Manage Subscriptions; on Android, the Play Store subscription page.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Optional verbs land alongside when the product needs them: a &lt;code&gt;purchaseOneTime(productId)&lt;/code&gt; for lifetime or consumable purchases, a &lt;code&gt;getAvailableProducts()&lt;/code&gt; when the paywall screen wants to render prices from the server. The four load-bearing verbs are the minimum.&lt;/p&gt;

&lt;p&gt;The invariant that makes the interface real is what happens in the &lt;em&gt;rest&lt;/em&gt; of the codebase: no other file imports RevenueCat, Adapty, Qonversion, StoreKit, or Play Billing types. The paywall screen imports &lt;code&gt;PaymentService&lt;/code&gt;. The settings screen imports &lt;code&gt;PaymentService&lt;/code&gt;. The account-restore modal imports &lt;code&gt;PaymentService&lt;/code&gt;. The provider lives in one adapter file — behind the interface — and everything else in the codebase reaches payments through the interface. That is the payment provider abstraction layer, and it is the whole thing. This is the same shape as in-app purchase abstraction layer patterns you may have seen in Stripe-first web codebases: the SDK is the payload, the interface is the pattern.&lt;/p&gt;

&lt;p&gt;Server-side entitlement is the second load-bearing beat. The &lt;code&gt;checkEntitlement()&lt;/code&gt; verb returns whatever the &lt;em&gt;server&lt;/em&gt; says, not what the local purchase state claims. The paywall SDK forwards a receipt to your backend; the backend validates it and writes entitlement to your database; the client reads that. Trust the phone for entitlement and you are one refund, one grace period, or one shared Apple ID away from a support ticket you cannot resolve. This is the correctness anchor for server-side receipt validation mobile MVP work — the boundary between "what the SDK returns" and "what the app trusts" is what the interface enforces.&lt;/p&gt;

&lt;p&gt;Building this before you have a paywall is cheap. AI writes the RevenueCat, Adapty, or Qonversion SDK wiring in minutes — the initialisation, purchase callbacks, receipt forwarding, all of it. What AI does not do is decide where to draw the interface, which verbs to expose, or whether entitlement lives on the phone or the server. That judgement — and the migration cost of getting it wrong — is what the senior engineer is paid for. See &lt;a href="https://msappdev.com/ai-delivery/" rel="noopener noreferrer"&gt;how AI-augmented delivery works in practice&lt;/a&gt; for the workflow behind it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why paying for a paywall SDK is orthogonal to whether the abstraction exists
&lt;/h2&gt;

&lt;p&gt;The counter-intuitive point most founders get wrong in the payments area: paying for a RevenueCat plan does not mean you have a mobile payment provider abstraction. RevenueCat is a very good implementation of the &lt;em&gt;adapter&lt;/em&gt;. It is not the interface. Founders skip the wrapper because their engineer said "RevenueCat handles all the platform stuff for us" — which is true, and irrelevant to whether swapping RevenueCat for Adapty next year will cost you one file or twelve.&lt;/p&gt;

&lt;p&gt;The revenuecat adapty qonversion abstraction confusion is worth naming directly. All three SDKs remove the &lt;em&gt;platform-specific&lt;/em&gt; pain: StoreKit calls, Play Billing calls, iOS-Android receipt-format mismatches, sandbox-vs-production edge cases, subscription state machines. They earn their monthly fee for that alone. None of them removes the &lt;em&gt;provider-specific&lt;/em&gt; pain of migrating between themselves, or of adding a second monetisation surface — a Stripe-powered web funnel next to App Store IAP, a lifetime purchase on top of a subscription, an enterprise deal that skips the App Store entirely. That pain is what the wrapper exists to remove, and no paywall SDK, by definition, can remove it — the wrapper's job is to sit above the SDK, not to be it.&lt;/p&gt;

&lt;p&gt;The layered picture makes the point clearer. The paywall SDK is Layer 3, the vendor-specific adapter. The wrapper is Layer 2, the app-facing interface. The callers — paywall, settings, restore flow, business logic — are Layer 1. All three layers exist regardless of your architectural choices; skipping Layer 2 collapses Layer 1 into Layer 3 and turns every screen into a migration touch point. Adding a payment provider abstraction layer costs one file up front and returns every provider swap for the life of the product.&lt;/p&gt;

&lt;p&gt;Concrete proof under production pressure: &lt;a href="https://msappdev.com/projects/boyfi/" rel="noopener noreferrer"&gt;Boyfi&lt;/a&gt; shipped with dual monetisation surfaces from the start — App Store IAP via Adapty and a custom Stripe-powered WebFunnels API for the web funnel — sitting behind one &lt;code&gt;PaymentService&lt;/code&gt; interface. The app store iap and stripe web funnel dual-subscription pattern is exactly the migration most codebases cannot survive without a rewrite; Boyfi added a third AI provider eight months later by touching one module, without touching a paywall screen. &lt;a href="https://msappdev.com/projects/mycoach/" rel="noopener noreferrer"&gt;MyCoach&lt;/a&gt; is the opposite proof: Qonversion behind the same wrapper shape, two years and sixty updates without touching the payment layer once. The wrapper is not less valuable when you do not swap providers — it is what makes the code readable and the test surface small when the payment side stays stable and everything around it moves.&lt;/p&gt;

&lt;p&gt;The discipline generalises. &lt;a href="https://msappdev.com/projects/hairly/" rel="noopener noreferrer"&gt;Hairly&lt;/a&gt; uses the same shape on image providers — three (OpenAI, RapidAPI, Lightx) behind one interface, two more added by writing one adapter each. The same pattern applies to auth — see the &lt;a href="https://msappdev.com/blog/auth-identity-decisions-haunt-mvp/" rel="noopener noreferrer"&gt;sibling deep-dive on auth-provider abstraction&lt;/a&gt; for the mirror argument. The wrapper is the pattern; the SDK is the payload.&lt;/p&gt;

&lt;p&gt;I have cleaned up codebases where the paywall SDK was called from twelve different screens. Adding a web-based subscription funnel alongside App Store IAP required touching every one of them — a rewrite, not a feature addition. The engineer who built that codebase held the same title, used the same SDK, and delivered on time. The difference between that codebase and one that survives the year-two migration is one file — the interface — that was never written.&lt;/p&gt;

&lt;h2&gt;
  
  
  The founder-side test: three questions to ask before the first paywall ships
&lt;/h2&gt;

&lt;p&gt;You do not need to read the code to verify the wrapper exists. You need to hear the answer to three questions. This is the founder-side test of a mobile payment provider abstraction, and it works in an architectural review or a hiring first call.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Question 1 — "Show me the file that hides the payment provider from the rest of the app."&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If the engineer opens &lt;code&gt;PaywallScreen.tsx&lt;/code&gt; (or &lt;code&gt;.dart&lt;/code&gt;, or &lt;code&gt;.swift&lt;/code&gt;) and starts pointing at RevenueCat imports and product-identifier lookups mixed with the pricing layout, the wrapper does not exist. If they open &lt;code&gt;PaymentService.ts&lt;/code&gt; or &lt;code&gt;payment_service.dart&lt;/code&gt; and it imports the SDK, exposes four verbs, and returns app-native types (a &lt;code&gt;Result&amp;lt;Entitlement, PaymentError&amp;gt;&lt;/code&gt; or an equivalent — not raw SDK objects), the wrapper exists. The file name is not the point. The point is a single answer: "here is the one file that touches the provider, and nothing else does."&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Question 2 — "If I asked you tomorrow to swap RevenueCat for Adapty, or add Stripe alongside App Store IAP, what would change?"&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This is the how to swap payment provider mobile app question, and the answer is the receipt for whether the interface was drawn. The right answer names one file — the adapter behind &lt;code&gt;PaymentService&lt;/code&gt; — plus webhook re-wiring on the backend. That is a one-day change. The wrong answer describes "touching the paywall screen, the settings screen, the restore flow, the account screen…" — that answer is the rewrite quote your engineer will present in month nine when the growth team asks for a web funnel. Ask it in week one instead.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Question 3 — "Where does entitlement come from — the phone or the server?"&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The right answer is the server. Webhook-driven, validated on your own backend, written to your own database, read by the client as a boolean or a small JSON blob. The wrong answer is "we trust the local purchase state" or "we read it from the SDK on app launch." The wrong answer ships fine for the first hundred users; it produces duplicate subscriptions, grace-period edge cases, and refund-window support tickets you cannot resolve at a hundred paying users and above.&lt;/p&gt;

&lt;p&gt;Three questions. Zero code-reading required. Together they surface whether the mobile payment provider abstraction was designed or skipped — and whether the rewrite tax is compounding quietly in a codebase that looks fine in the demo. It is fixable in month nine. It is far cheaper to fix in week one.&lt;/p&gt;

&lt;h2&gt;
  
  
  The engagement, if you want the boundary drawn for you
&lt;/h2&gt;

&lt;p&gt;Keeping the models separable has a second payoff at reporting time. Three billing mechanics running in parallel is a join, not a dashboard reading, and answering "which model is dragging revenue down" is only cheap if the data underneath is cleanly separated — see &lt;a href="https://msappdev.com/blog/mcp-mobile-app-analytics/" rel="noopener noreferrer"&gt;MCP for mobile app analytics&lt;/a&gt; for what that looks like on a live app.&lt;/p&gt;

&lt;p&gt;The wrapper is cheap before the SDK is picked, expensive to retrofit after the twelfth screen calls the SDK directly. If you are staring at a paywall SDK decision, a dual-monetisation question, or a codebase where you suspect Layer 2 got skipped — &lt;a href="https://msappdev.com/#contact" rel="noopener noreferrer"&gt;tell me about your project&lt;/a&gt; and I will tell you whether the mobile payment provider abstraction is already implied by your scope, or the decision your engineer is about to skip. For the full engagement model, the &lt;a href="https://msappdev.com/services/" rel="noopener noreferrer"&gt;services page&lt;/a&gt; is where payments — and the other seven architectural areas — are owned end-to-end.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="https://msappdev.com/blog/payment-provider-abstraction-day-one/?utm_source=devto&amp;amp;utm_medium=social&amp;amp;utm_campaign=payment-provider-abstraction-day-one" rel="noopener noreferrer"&gt;msappdev.com&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>payments</category>
      <category>architecture</category>
      <category>mvp</category>
    </item>
    <item>
      <title>MCP for Mobile App Analytics: A Live Case Study</title>
      <dc:creator>Mikalai Siliuk</dc:creator>
      <pubDate>Mon, 28 Sep 2026 12:09:06 +0000</pubDate>
      <link>https://dev.to/mikalaisiliuk/mcp-for-mobile-app-analytics-a-live-case-study-m6l</link>
      <guid>https://dev.to/mikalaisiliuk/mcp-for-mobile-app-analytics-a-live-case-study-m6l</guid>
      <description>&lt;p&gt;Most funded mobile apps end up with four or five analytics tools wired in before anyone asks whether that's too many. Amplitude for behavior. Stripe for revenue. AppsFlyer for attribution. BigQuery for the exports nothing else covers. Each one answers its own question well. None of them answers the question a founder actually asks out loud: why did revenue dip last week. This is a case study in closing that gap with MCP — mobile app analytics you can actually ask questions of, on a production app rather than a demo.&lt;/p&gt;

&lt;p&gt;I ran into this on a real-time communication platform I've led mobile delivery on for the past year — 1,000+ daily active users, 3,000+ audio and video calls a month, all under NDA. The stack was already solid: Amplitude, Customer.io, AppsFlyer, and BigQuery, each doing its job. The gap wasn't data. It was the cross-referencing of four exports before anyone could trust an answer.&lt;/p&gt;

&lt;p&gt;The full architecture behind those numbers is in &lt;a href="https://msappdev.com/cases/companion/" rel="noopener noreferrer"&gt;the real-time communication platform case study&lt;/a&gt;. This piece is about one layer on top of it.&lt;/p&gt;

&lt;h2&gt;
  
  
  What MCP is, in one paragraph
&lt;/h2&gt;

&lt;p&gt;Model Context Protocol is a standard way to give an AI assistant direct, permissioned access to a system — a database, an API, an analytics tool — so it can query live data instead of guessing from whatever it was trained on. In practice it is a small server — one you can write yourself — that sits between the assistant and your own backend and translates a question into the query your schema understands. Nothing about your data leaves your infrastructure. Nothing gets a new dashboard. The assistant just gains the ability to read what you already have.&lt;/p&gt;

&lt;p&gt;That is the whole idea, and the reason it matters here is narrow: the bottleneck in most mobile app analytics stacks is not collection, and not storage. It is the human in the middle, assembling.&lt;/p&gt;

&lt;h2&gt;
  
  
  The problem MCP actually solves in a mobile app analytics stack
&lt;/h2&gt;

&lt;p&gt;The business had shipped three monetization models in sequence — token-based billing, then tiered subscriptions with daily limits, then moment packs — with thousands of existing users still on each one, which means all three now run in parallel. Revenue grew 40% in the 90 days after the third version launched. Good news, but the honest follow-up question — which of the three models is driving that, and which one is quietly dragging it down — didn't have a fast answer. It lived across a Stripe export, an Amplitude cohort, and a spreadsheet someone updated by hand.&lt;/p&gt;

&lt;p&gt;So I wrote our own MCP server — against our backend, not against the vendors. Everything the product knows about itself already lives in that database: subscription state, billing events, call records, usage, the operator panel's own collections. What it lacked was any way to ask it a question without hand-writing a query and post-processing the result in code — and on a document store there is no join and no ad-hoc aggregation across collections, so the interesting questions are precisely the ones a single query cannot express. The server made the whole of it queryable in plain language, which matters more than it sounds: the useful questions rarely sit inside one collection, and a connector wired to a single vendor can only ever answer the part that vendor happens to hold. "Which pricing version is underperforming this week" stopped being a Tuesday-afternoon investigation. It became a question typed into a chat window, answered against live data, not a stale export from Monday.&lt;/p&gt;

&lt;p&gt;Worth being precise about what changed, because it is easy to oversell. Nothing about the mobile app analytics itself improved — the data was always there. The queries were always writable. What went away was the assembly step — pulling three sources, lining them up on matching date ranges and user cohorts, and hoping nothing shifted between the first export and the third. That step is where the delay lived, and it is also where the errors lived, because a hand-aligned spreadsheet is a hand-aligned spreadsheet.&lt;/p&gt;

&lt;p&gt;Running several pricing models at once is what makes this acute. One model is a single number you can read off a dashboard. Three overlapping cohorts on three different billing mechanics is a join, and joins are exactly what nobody wants to do by hand on a Tuesday. The architectural side of keeping those models separable is a different problem, argued in &lt;a href="https://msappdev.com/blog/payment-provider-abstraction-day-one/" rel="noopener noreferrer"&gt;why payment provider abstraction belongs on day one&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Getting vendor data into a database you own
&lt;/h3&gt;

&lt;p&gt;The obvious objection: most of that data lives in Amplitude and Stripe, not in&lt;br&gt;
your own store. It does — and every one of those vendors documents a way out.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Amplitude&lt;/strong&gt; exports event data via its Export API (gzipped JSON, 4 GB per
request, so hourly windows for large ranges), via a scheduled Amazon S3
export available even on free plans, or straight into Snowflake, BigQuery or
Redshift as a warehouse destination.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Stripe Data Pipeline&lt;/strong&gt; syncs to Snowflake, Redshift, Databricks, S3 or GCS
with a refresh every few hours, and carries report-level metrics the plain
REST API does not expose.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AppsFlyer Data Locker&lt;/strong&gt; drops raw, user-level attribution data into a
bucket you own — S3, GCS, BigQuery or Snowflake — with the Pull API for
on-demand slices.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Customer.io&lt;/strong&gt; has a warehouse sync that lands parquet files for deliveries,
metrics and people in your S3 or GCS bucket, plus reporting webhooks for
real-time events.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;None of this is exotic; it is the standard "get your own data out" path each&lt;br&gt;
vendor already supports. The work is landing it on a schedule and giving it a&lt;br&gt;
schema your MCP server can reason about. That is the actual build — the&lt;br&gt;
assistant part is comparatively trivial once the data has a home.&lt;/p&gt;

&lt;h2&gt;
  
  
  A second example: call reliability
&lt;/h2&gt;

&lt;p&gt;The same pattern showed up on the product side. Calls are the core of the product, and a single dropped call can lose a paying user for good. The platform runs a heartbeat watchdog that monitors every active call server-side and auto-terminates when balance runs out, with a grace period under 60 seconds. Call completion sits above 97% across 3,000+ monthly calls.&lt;/p&gt;

&lt;p&gt;That number is easy to state and hard to interrogate. Is 97% stable, or is it drifting? Is the drop concentrated in one region, or one type of device? Before the MCP layer, answering that meant pulling call records out of the backend by hand, lining them up against session data, and a fair amount of squinting. After, it's one query against live call data.&lt;/p&gt;

&lt;p&gt;The difference is not that the answer became available. It is that the answer became cheap enough to ask casually. A question that has to be assembled first gets asked once something is already visibly wrong. A question you can simply type gets asked while it is still a two-point drift — which is the only point at which a two-point drift is fixable.&lt;/p&gt;

&lt;h2&gt;
  
  
  What your mobile app analytics needs first
&lt;/h2&gt;

&lt;p&gt;This does not work on a messy stack, and it is worth saying so before anyone treats it as a shortcut.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Your events have to mean something.&lt;/strong&gt; This is the one prerequisite that decides whether the whole layer is trustworthy. An assistant querying &lt;code&gt;paywall_shown&lt;/code&gt;, &lt;code&gt;paywall_view&lt;/code&gt;, and &lt;code&gt;paywall_displayed&lt;/code&gt; across three tools will confidently give you three different answers. The naming discipline that makes this layer trustworthy is the same one that makes your own dashboards trustworthy, and it is cheap in week one and expensive in month six — see &lt;a href="https://msappdev.com/blog/analytics-event-taxonomy-mobile/" rel="noopener noreferrer"&gt;the day-one event taxonomy most founders skip&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Read access has to be scoped deliberately.&lt;/strong&gt; The server sits on the database holding your revenue and user data, which is exactly why it gets read-only keys, scoped to the collections and date ranges that answer business questions and nothing else — pointed at a reporting replica rather than the primary, if you have one. This is a normal access-control decision, not a novel one, but it is the decision people skip when they are excited about the demo.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Someone has to still know what the numbers mean.&lt;/strong&gt; The layer removes the assembly work. It does not remove the judgment. "Revenue is up 40%" and "revenue is up 40% because one cohort's annual renewals landed in the same week" are different sentences, and only one of them is a strategy.&lt;/p&gt;

&lt;h2&gt;
  
  
  What this changes, practically
&lt;/h2&gt;

&lt;p&gt;It didn't replace the mobile app analytics dashboards — Amplitude, Stripe, and BigQuery still exist for what they're built for. Scheduled reports, alerting, the charts a supervisor keeps open all day: those stay. It replaced the manual assembly step in between: the part where a founder or a supervisor pulls three sources, lines them up, and hopes nothing shifted in the meantime.&lt;/p&gt;

&lt;p&gt;This is what AI-augmented delivery looks like once the novelty wears off — not code written faster, but questions answered faster by the person who already knows the system. &lt;a href="https://msappdev.com/ai-delivery/" rel="noopener noreferrer"&gt;The delivery model behind that&lt;/a&gt; is the same one that shipped the platform in the first place.&lt;/p&gt;

&lt;p&gt;This isn't a one-time build. It's the kind of thing that keeps paying off as a product's data surface grows — every new integration is another collection the MCP layer can already read, not another dashboard someone has to remember to check. The stack that started as Amplitude and Stripe is now five tools deep, and the marginal cost of the sixth is a connector rather than a new tab in someone's browser and a new place to forget to look.&lt;/p&gt;

&lt;p&gt;If your app already has the mobile app analytics stack and the questions are still taking longer to answer than they should, that's usually not a data problem. It's worth a conversation before it's worth another tool — &lt;a href="https://msappdev.com/#contact" rel="noopener noreferrer"&gt;tell me what you're trying to answer&lt;/a&gt;, or see &lt;a href="https://msappdev.com/hire-mobile-app-developer/" rel="noopener noreferrer"&gt;how I work with teams already running in production&lt;/a&gt;.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="https://msappdev.com/blog/mcp-mobile-app-analytics/?utm_source=devto&amp;amp;utm_medium=social&amp;amp;utm_campaign=mcp-mobile-app-analytics" rel="noopener noreferrer"&gt;msappdev.com&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>analytics</category>
      <category>ai</category>
      <category>architecture</category>
    </item>
  </channel>
</rss>
